Upload files to "Source"
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
#include "PluginProcessor.h"
|
||||
#include "PluginEditor.h"
|
||||
#include "ScopeComponent.h"
|
||||
#include "WavetableOsc.h"
|
||||
#include <array>
|
||||
|
||||
//==============================================================================
|
||||
NeuralSynthAudioProcessorEditor::NeuralSynthAudioProcessorEditor (NeuralSynthAudioProcessor& p)
|
||||
@@ -12,13 +14,9 @@ NeuralSynthAudioProcessorEditor::NeuralSynthAudioProcessorEditor (NeuralSynthAud
|
||||
|
||||
addAndMakeVisible(mainScopeComponent);
|
||||
|
||||
waveformSelector.setModel(&waveformContents);
|
||||
waveformContents.onSelect = [this](int row)
|
||||
{
|
||||
// write to the parameter so voices update safely
|
||||
audioProcessor.parameters.getParameterAsValue("waveform") = (float)juce::jlimit(0, 3, row);
|
||||
};
|
||||
addAndMakeVisible(waveformSelector);
|
||||
presetMenuButton.setButtonText("Preset");
|
||||
presetMenuButton.onClick = [this] { showPresetMenu(); };
|
||||
addAndMakeVisible(presetMenuButton);
|
||||
|
||||
// --- Panels ---
|
||||
adsrComponent.emplace(tree, "adsr", "Amp Env");
|
||||
@@ -92,6 +90,76 @@ NeuralSynthAudioProcessorEditor::NeuralSynthAudioProcessorEditor (NeuralSynthAud
|
||||
});
|
||||
addAndMakeVisible(*filterEnvComponent);
|
||||
|
||||
auto configureBankSlider = [](juce::Slider* slider)
|
||||
{
|
||||
if (slider == nullptr)
|
||||
return;
|
||||
|
||||
const auto& presets = WT::FactoryLibrary::get();
|
||||
std::vector<juce::String> names;
|
||||
names.reserve(presets.size());
|
||||
for (const auto& preset : presets)
|
||||
names.push_back(preset.name);
|
||||
|
||||
const double maxIndex = names.empty() ? 0.0 : (double) (names.size() - 1);
|
||||
slider->setRange (0.0, maxIndex, 1.0);
|
||||
slider->setNumDecimalPlacesToDisplay(0);
|
||||
|
||||
slider->textFromValueFunction = [names](double value)
|
||||
{
|
||||
if (names.empty())
|
||||
return juce::String ((int) std::lround (value));
|
||||
|
||||
const int idx = juce::jlimit (0, (int) names.size() - 1, (int) std::lround (value));
|
||||
return names[(size_t) idx];
|
||||
};
|
||||
|
||||
slider->valueFromTextFunction = [names](const juce::String& text)
|
||||
{
|
||||
if (! names.empty())
|
||||
{
|
||||
for (size_t i = 0; i < names.size(); ++i)
|
||||
if (text.equalsIgnoreCase (names[i]))
|
||||
return (double) i;
|
||||
}
|
||||
return text.getDoubleValue();
|
||||
};
|
||||
};
|
||||
|
||||
auto configureShapeSlider = [](juce::Slider* slider)
|
||||
{
|
||||
if (slider == nullptr)
|
||||
return;
|
||||
|
||||
static const std::array<juce::String, 4> shapeNames { "Sine", "Triangle", "Ramp Up", "Ramp Down" };
|
||||
slider->setNumDecimalPlacesToDisplay(0);
|
||||
slider->textFromValueFunction = [](double value)
|
||||
{
|
||||
const int idx = juce::jlimit (0, (int) shapeNames.size() - 1, (int) std::lround (value));
|
||||
return shapeNames[(size_t) idx];
|
||||
};
|
||||
slider->valueFromTextFunction = [](const juce::String& text)
|
||||
{
|
||||
for (size_t i = 0; i < shapeNames.size(); ++i)
|
||||
if (text.equalsIgnoreCase (shapeNames[i]))
|
||||
return (double) i;
|
||||
return text.getDoubleValue();
|
||||
};
|
||||
};
|
||||
|
||||
wtComponent.emplace(tree, "wt", "Layer A");
|
||||
configureBankSlider (wtComponent->getSlider("bank"));
|
||||
configureShapeSlider (wtComponent->getSlider("lfoShape"));
|
||||
addAndMakeVisible(*wtComponent);
|
||||
wtComponent->setTitleText("Layer A");
|
||||
|
||||
layerSelector.addItem("Layer A", 1);
|
||||
layerSelector.addItem("Layer B", 2);
|
||||
layerSelector.setSelectedId(1, juce::dontSendNotification);
|
||||
layerSelector.onChange = [this] { handleLayerSelectionChanged(); };
|
||||
wtComponent->setTopBarAccessory(&layerSelector, 118);
|
||||
handleLayerSelectionChanged();
|
||||
|
||||
// Master fader + label
|
||||
addAndMakeVisible(masterLevelSlider);
|
||||
masterLevelLabel.setText("Master", juce::dontSendNotification);
|
||||
@@ -102,18 +170,22 @@ NeuralSynthAudioProcessorEditor::NeuralSynthAudioProcessorEditor (NeuralSynthAud
|
||||
masterLevelLabel.setJustificationType(juce::Justification::centred);
|
||||
addAndMakeVisible(masterLevelLabel);
|
||||
|
||||
// Blank placeholder
|
||||
addAndMakeVisible(blankPanel);
|
||||
|
||||
// Attach master parameter
|
||||
gainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(
|
||||
audioProcessor.parameters, "master", masterLevelSlider.slider);
|
||||
|
||||
lastPresetIndex = audioProcessor.getCurrentPresetIndex();
|
||||
updatePresetButtonLabel();
|
||||
startTimerHz(10);
|
||||
|
||||
setSize(1400, 720);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
NeuralSynthAudioProcessorEditor::~NeuralSynthAudioProcessorEditor() = default;
|
||||
NeuralSynthAudioProcessorEditor::~NeuralSynthAudioProcessorEditor()
|
||||
{
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthAudioProcessorEditor::paint (juce::Graphics& g)
|
||||
@@ -124,47 +196,147 @@ void NeuralSynthAudioProcessorEditor::paint (juce::Graphics& g)
|
||||
//==============================================================================
|
||||
void NeuralSynthAudioProcessorEditor::resized()
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced(16);
|
||||
auto outer = getLocalBounds().reduced(16);
|
||||
|
||||
// --- carve out sidebar for MASTER (right side) --------------------------
|
||||
const int sidebarWidth = 160; // tweak if you want it wider/narrower
|
||||
auto gridArea = outer;
|
||||
auto sidebar = gridArea.removeFromRight(sidebarWidth).reduced(8);
|
||||
|
||||
// Master label + fader in the sidebar (stacked)
|
||||
{
|
||||
auto top = sidebar.removeFromTop(24);
|
||||
masterLevelLabel.setBounds(top.withTrimmedLeft(4));
|
||||
|
||||
// leave a little top margin before the fader
|
||||
sidebar.removeFromTop(8);
|
||||
masterLevelSlider.setBounds(sidebar);
|
||||
}
|
||||
|
||||
// --- Grid: Scope + two rows of five boxes (no gaps) ---------------------
|
||||
juce::Grid grid;
|
||||
|
||||
grid.templateRows = {
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(20)), // scope row
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(40)), // row 1
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(40)) // row 2
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(22)), // scope band
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(39)), // row 1
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(78)) // row 2 (wider to absorb layer panel)
|
||||
};
|
||||
|
||||
// 6 columns: 5 content + 1 sidebar (waveform+master)
|
||||
grid.templateColumns = {
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(18)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(18)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(18)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(18)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(18)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(10))
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1))
|
||||
};
|
||||
|
||||
// Row 0
|
||||
grid.items.add(juce::GridItem(mainScopeComponent)
|
||||
.withArea(juce::GridItem::Span(1), juce::GridItem::Span(5)));
|
||||
grid.items.add(juce::GridItem(waveformSelector)
|
||||
.withArea(juce::GridItem::Span(1), juce::GridItem::Span(1)));
|
||||
grid.rowGap = juce::Grid::Px(0);
|
||||
grid.columnGap = juce::Grid::Px(0);
|
||||
|
||||
// Row 1
|
||||
grid.items.add(juce::GridItem(*adsrComponent));
|
||||
grid.items.add(juce::GridItem(*chorusComponent));
|
||||
grid.items.add(juce::GridItem(*delayComponent));
|
||||
grid.items.add(juce::GridItem(*reverbComponent));
|
||||
grid.items.add(juce::GridItem(*eqComponent));
|
||||
grid.items.add(juce::GridItem(masterLevelLabel));
|
||||
grid.items.clear();
|
||||
|
||||
// Row 2
|
||||
grid.items.add(juce::GridItem(*flangerComponent));
|
||||
grid.items.add(juce::GridItem(*distortionComponent));
|
||||
grid.items.add(juce::GridItem(*filterComponent));
|
||||
grid.items.add(juce::GridItem(*filterEnvComponent));
|
||||
grid.items.add(juce::GridItem(blankPanel));
|
||||
grid.items.add(juce::GridItem(masterLevelSlider));
|
||||
// Row 1 (scope row)
|
||||
grid.items.add(juce::GridItem(mainScopeComponent).withArea(1, 1, 1, 5));
|
||||
// Put preset button at the top-right cell of the scope row
|
||||
grid.items.add(juce::GridItem(presetMenuButton)
|
||||
.withArea(1, 5)
|
||||
.withJustifySelf(juce::GridItem::JustifySelf::end)
|
||||
.withAlignSelf(juce::GridItem::AlignSelf::start));
|
||||
|
||||
grid.performLayout(bounds);
|
||||
// Row 2 (top row of panels): Amp Env, Chorus, Delay, Reverb, EQ
|
||||
grid.items.add(juce::GridItem(*adsrComponent ).withArea(2, 1));
|
||||
grid.items.add(juce::GridItem(*chorusComponent ).withArea(2, 2));
|
||||
grid.items.add(juce::GridItem(*delayComponent ).withArea(2, 3));
|
||||
grid.items.add(juce::GridItem(*reverbComponent ).withArea(2, 4));
|
||||
grid.items.add(juce::GridItem(*eqComponent ).withArea(2, 5));
|
||||
|
||||
// Row 3 (bottom row of panels): Flanger, Distortion, Filter, Filter Env, Wavetable
|
||||
grid.items.add(juce::GridItem(*flangerComponent ).withArea(3, 1));
|
||||
grid.items.add(juce::GridItem(*distortionComponent).withArea(3, 2));
|
||||
grid.items.add(juce::GridItem(*filterComponent ).withArea(3, 3));
|
||||
grid.items.add(juce::GridItem(*filterEnvComponent ).withArea(3, 4));
|
||||
grid.items.add(juce::GridItem(*wtComponent ).withArea(3, 5));
|
||||
|
||||
grid.performLayout(gridArea);
|
||||
}
|
||||
|
||||
void NeuralSynthAudioProcessorEditor::timerCallback()
|
||||
{
|
||||
const int current = audioProcessor.getCurrentPresetIndex();
|
||||
if (current != lastPresetIndex)
|
||||
{
|
||||
lastPresetIndex = current;
|
||||
updatePresetButtonLabel();
|
||||
}
|
||||
}
|
||||
|
||||
void NeuralSynthAudioProcessorEditor::showPresetMenu()
|
||||
{
|
||||
const auto& presets = audioProcessor.getFactoryPresets();
|
||||
if (presets.empty())
|
||||
return;
|
||||
|
||||
juce::PopupMenu menu;
|
||||
juce::StringArray categories;
|
||||
for (const auto& preset : presets)
|
||||
if (! categories.contains(preset.category))
|
||||
categories.add(preset.category);
|
||||
|
||||
const int baseId = 1000;
|
||||
for (const auto& category : categories)
|
||||
{
|
||||
juce::PopupMenu sub;
|
||||
for (int i = 0; i < (int) presets.size(); ++i)
|
||||
{
|
||||
if (presets[(size_t) i].category == category)
|
||||
{
|
||||
sub.addItem(baseId + i, presets[(size_t) i].name,
|
||||
true, audioProcessor.getCurrentPresetIndex() == i);
|
||||
}
|
||||
}
|
||||
menu.addSubMenu(category, sub);
|
||||
}
|
||||
|
||||
menu.showMenuAsync(juce::PopupMenu::Options().withParentComponent(this),
|
||||
[this, baseId](int result)
|
||||
{
|
||||
if (result >= baseId)
|
||||
{
|
||||
const int index = result - baseId;
|
||||
audioProcessor.applyPreset(index);
|
||||
lastPresetIndex = index;
|
||||
updatePresetButtonLabel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void NeuralSynthAudioProcessorEditor::updatePresetButtonLabel()
|
||||
{
|
||||
const auto& presets = audioProcessor.getFactoryPresets();
|
||||
const int current = audioProcessor.getCurrentPresetIndex();
|
||||
|
||||
juce::String label = "Preset: ";
|
||||
if (current >= 0 && current < (int) presets.size())
|
||||
{
|
||||
const auto& preset = presets[(size_t) current];
|
||||
label += preset.category + " / " + preset.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
label += "Custom";
|
||||
}
|
||||
|
||||
presetMenuButton.setButtonText(label);
|
||||
}
|
||||
|
||||
void NeuralSynthAudioProcessorEditor::handleLayerSelectionChanged()
|
||||
{
|
||||
const bool useLayerB = (layerSelector.getSelectedId() == 2);
|
||||
if (! wtComponent || controllingLayerB == useLayerB)
|
||||
return;
|
||||
|
||||
controllingLayerB = useLayerB;
|
||||
const std::string group = useLayerB ? "wt2" : "wt";
|
||||
wtComponent->reassignParamGroup(group);
|
||||
wtComponent->setTitleText(useLayerB ? "Layer B" : "Layer A");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user