mirror of
https://codeberg.org/armin/chromaflock.git
synced 2026-09-01 04:10:47 +02:00
Fix menu scaling issues, fix preset menu layout, add LED display (to
show patch), add prev/next patch buttons
This commit is contained in:
parent
827eca7e61
commit
79be724e5d
5 changed files with 281 additions and 175 deletions
|
|
@ -291,9 +291,7 @@ void SpectrumAnalyzer::paint(juce::Graphics& g) {
|
|||
juce::Graphics::ScopedSaveState saved(g);
|
||||
g.setOpacity(0.7f);
|
||||
|
||||
juce::ColourGradient barGrad(juce::Colour(0xff00ff66), 0.0f, by,
|
||||
juce::Colour(0xff005522), 0.0f, barBottom, false);
|
||||
g.setGradientFill(barGrad);
|
||||
g.setColour(juce::Colour(0xff00ff66));
|
||||
g.fillRect(bx, by, bw, barH);
|
||||
}
|
||||
}
|
||||
|
|
@ -303,6 +301,102 @@ void SpectrumAnalyzer::paint(juce::Graphics& g) {
|
|||
g.drawText("SPECTRUM", bounds.withBottom(bounds.getY() + 22.0f).translated(0, 6), juce::Justification::centred);
|
||||
}
|
||||
|
||||
// --- Patch LCD (green dot-matrix) ---
|
||||
namespace {
|
||||
struct Glyph { char c; unsigned char rows[7]; };
|
||||
// 5x7 font; each byte is a row, bits 4..0 are columns left->right.
|
||||
const Glyph font5x7[] = {
|
||||
{' ', {0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
|
||||
{'-', {0x00,0x00,0x00,0x1F,0x00,0x00,0x00}},
|
||||
{'A', {0x0E,0x11,0x11,0x1F,0x11,0x11,0x11}},
|
||||
{'B', {0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E}},
|
||||
{'C', {0x0E,0x11,0x10,0x10,0x10,0x11,0x0E}},
|
||||
{'D', {0x1E,0x11,0x11,0x11,0x11,0x11,0x1E}},
|
||||
{'E', {0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F}},
|
||||
{'F', {0x1F,0x10,0x10,0x1E,0x10,0x10,0x10}},
|
||||
{'G', {0x0E,0x11,0x10,0x17,0x11,0x11,0x0F}},
|
||||
{'H', {0x11,0x11,0x11,0x1F,0x11,0x11,0x11}},
|
||||
{'I', {0x0E,0x04,0x04,0x04,0x04,0x04,0x0E}},
|
||||
{'J', {0x07,0x02,0x02,0x02,0x02,0x12,0x0C}},
|
||||
{'K', {0x11,0x12,0x14,0x18,0x14,0x12,0x11}},
|
||||
{'L', {0x10,0x10,0x10,0x10,0x10,0x10,0x1F}},
|
||||
{'M', {0x11,0x1B,0x15,0x15,0x11,0x11,0x11}},
|
||||
{'N', {0x11,0x11,0x19,0x15,0x13,0x11,0x11}},
|
||||
{'O', {0x0E,0x11,0x11,0x11,0x11,0x11,0x0E}},
|
||||
{'P', {0x1E,0x11,0x11,0x1E,0x10,0x10,0x10}},
|
||||
{'Q', {0x0E,0x11,0x11,0x11,0x15,0x12,0x0D}},
|
||||
{'R', {0x1E,0x11,0x11,0x1E,0x14,0x12,0x11}},
|
||||
{'S', {0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E}},
|
||||
{'T', {0x1F,0x04,0x04,0x04,0x04,0x04,0x04}},
|
||||
{'U', {0x11,0x11,0x11,0x11,0x11,0x11,0x0E}},
|
||||
{'V', {0x11,0x11,0x11,0x11,0x11,0x0A,0x04}},
|
||||
{'W', {0x11,0x11,0x11,0x15,0x15,0x1B,0x11}},
|
||||
{'X', {0x11,0x11,0x0A,0x04,0x0A,0x11,0x11}},
|
||||
{'Y', {0x11,0x11,0x0A,0x04,0x04,0x04,0x04}},
|
||||
{'Z', {0x1F,0x01,0x02,0x04,0x08,0x10,0x1F}},
|
||||
{'0', {0x0E,0x13,0x15,0x15,0x19,0x11,0x0E}},
|
||||
{'1', {0x04,0x0C,0x04,0x04,0x04,0x04,0x0E}},
|
||||
{'2', {0x0E,0x11,0x01,0x06,0x08,0x10,0x1F}},
|
||||
{'3', {0x1F,0x02,0x04,0x02,0x01,0x11,0x0E}},
|
||||
{'4', {0x02,0x06,0x0A,0x12,0x1F,0x02,0x02}},
|
||||
{'5', {0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E}},
|
||||
{'6', {0x06,0x08,0x10,0x1E,0x11,0x11,0x0E}},
|
||||
{'7', {0x1F,0x01,0x02,0x04,0x08,0x08,0x08}},
|
||||
{'8', {0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E}},
|
||||
{'9', {0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C}},
|
||||
};
|
||||
|
||||
const unsigned char* getFontGlyph(char c) {
|
||||
for (const auto& g : font5x7)
|
||||
if (g.c == c) return g.rows;
|
||||
return font5x7[0].rows; // space fallback
|
||||
}
|
||||
}
|
||||
|
||||
void PatchLCD::paint(juce::Graphics& g) {
|
||||
auto b = getLocalBounds();
|
||||
|
||||
// Dark LCD panel
|
||||
g.setColour(juce::Colour(0xff061206));
|
||||
g.fillRoundedRectangle(b.toFloat(), 3.0f);
|
||||
g.setColour(juce::Colour(0xff114411));
|
||||
g.drawRoundedRectangle(b.toFloat(), 3.0f, 1.0f);
|
||||
|
||||
const int pitch = 3;
|
||||
const int glyphW = 5, glyphH = 7, glyphGap = 1;
|
||||
|
||||
// Faint "off" LED grid
|
||||
g.setColour(juce::Colour(0xff0a200a));
|
||||
for (int y = pitch / 2; y < b.getHeight(); y += pitch)
|
||||
for (int x = pitch / 2; x < b.getWidth(); x += pitch)
|
||||
g.fillRect(static_cast<float>(x) - 0.5f, static_cast<float>(y) - 0.5f, 1.0f, 1.0f);
|
||||
|
||||
int y0 = (b.getHeight() - glyphH * pitch) / 2;
|
||||
int cx = 5;
|
||||
int cy = y0;
|
||||
if (cy < 0) cy = 0;
|
||||
|
||||
juce::String u = text.toUpperCase();
|
||||
for (auto chr : u) {
|
||||
const unsigned char* g7 = getFontGlyph(chr);
|
||||
for (int row = 0; row < glyphH; ++row) {
|
||||
unsigned char bits = g7[row];
|
||||
for (int col = 0; col < glyphW; ++col) {
|
||||
if (bits & (1 << (glyphW - 1 - col))) {
|
||||
float px = static_cast<float>(cx + col * pitch + pitch / 2);
|
||||
float py = static_cast<float>(cy + row * pitch + pitch / 2);
|
||||
g.setColour(juce::Colour(0xff1a330a));
|
||||
g.fillEllipse(px - pitch * 0.5f, py - pitch * 0.5f, pitch, pitch);
|
||||
g.setColour(juce::Colour(0xff33ff66));
|
||||
g.fillEllipse(px - pitch * 0.4f, py - pitch * 0.4f, pitch * 0.8f, pitch * 0.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
cx += (glyphW + glyphGap) * pitch;
|
||||
if (cx > b.getWidth()) break;
|
||||
}
|
||||
}
|
||||
|
||||
MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
|
||||
: processorRef(p), vuMeter(p), waveformDisplay(p), spectrumAnalyzer(p), pianoRoll(p) {
|
||||
|
||||
|
|
@ -386,19 +480,47 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
|
|||
setupParam(delayTimeKnob, delayTimeAttach, "delayTime", "TIME");
|
||||
setupParam(delayFbKnob, delayFbAttach, "delayFeedback", "FBACK");
|
||||
setupParam(delayMixKnob, delayMixAttach, "delayMix", "MIX");
|
||||
setupCB(delayPingPongBox, delayPingPongAttach, "delayPingPong", {"Off", "On"});
|
||||
setupParam(delaySpreadKnob, delaySpreadAttach, "delaySpread", "SPREAD");
|
||||
setupParam(reverbSizeKnob, reverbSizeAttach, "reverbSize", "SIZE");
|
||||
setupParam(reverbDampKnob, reverbDampAttach, "reverbDamping", "DAMP");
|
||||
setupParam(reverbMixKnob, reverbMixAttach, "reverbMix", "MIX");
|
||||
|
||||
// Preset button
|
||||
// Preset section: title button + dot-matrix LCD + prev/next
|
||||
presetButton.setColour(juce::TextButton::buttonColourId, juce::Colour(0xff2a2a2a));
|
||||
presetButton.setColour(juce::TextButton::textColourOffId, juce::Colour(0xffccaa44));
|
||||
presetButton.setLookAndFeel(&comboLaf);
|
||||
presetButton.onClick = [this]() { showPresetMenu(); };
|
||||
addAndMakeVisible(presetButton);
|
||||
|
||||
addAndMakeVisible(patchLCD);
|
||||
|
||||
auto setupArrow = [&](juce::TextButton& btn) {
|
||||
btn.setColour(juce::TextButton::buttonColourId, juce::Colour(0xff2a2a2a));
|
||||
btn.setColour(juce::TextButton::textColourOffId, juce::Colour(0xffccaa44));
|
||||
btn.setLookAndFeel(&comboLaf);
|
||||
addAndMakeVisible(btn);
|
||||
};
|
||||
setupArrow(prevPresetButton);
|
||||
setupArrow(nextPresetButton);
|
||||
|
||||
prevPresetButton.onClick = [this]() {
|
||||
auto& presets = processorRef.presetManager.getPresets();
|
||||
if (presets.empty()) return;
|
||||
currentPresetIndex = (currentPresetIndex - 1 + static_cast<int>(presets.size())) % static_cast<int>(presets.size());
|
||||
applyCurrentPreset();
|
||||
};
|
||||
nextPresetButton.onClick = [this]() {
|
||||
auto& presets = processorRef.presetManager.getPresets();
|
||||
if (presets.empty()) return;
|
||||
currentPresetIndex = (currentPresetIndex + 1) % static_cast<int>(presets.size());
|
||||
applyCurrentPreset();
|
||||
};
|
||||
|
||||
auto& presets = processorRef.presetManager.getPresets();
|
||||
if (!presets.empty()) {
|
||||
currentPresetIndex = 0;
|
||||
patchLCD.setText(presets[currentPresetIndex].name);
|
||||
}
|
||||
|
||||
scaleLabel.setText("SCALE", juce::dontSendNotification);
|
||||
scaleLabel.setFont(juce::Font(9.0f));
|
||||
scaleLabel.setColour(juce::Label::textColourId, juce::Colour(0xff888888));
|
||||
|
|
@ -462,29 +584,41 @@ void MainContentComponent::setupCombo(juce::ComboBox& combo) {
|
|||
|
||||
void MainContentComponent::showPresetMenu() {
|
||||
juce::PopupMenu menu;
|
||||
std::vector<juce::String> menuOrder;
|
||||
|
||||
auto categories = processorRef.presetManager.getCategories();
|
||||
int menuId = 1;
|
||||
|
||||
for (auto& cat : categories) {
|
||||
juce::PopupMenu subMenu;
|
||||
auto presets = processorRef.presetManager.getPresetsInCategory(cat);
|
||||
for (auto& preset : presets) {
|
||||
subMenu.addItem(menuId++, preset.name);
|
||||
menuOrder.push_back(preset.name);
|
||||
subMenu.addItem(static_cast<int>(menuOrder.size()), preset.name);
|
||||
}
|
||||
menu.addSubMenu(cat, subMenu);
|
||||
}
|
||||
|
||||
menu.showMenuAsync(juce::PopupMenu::Options(), [this](int result) {
|
||||
if (result > 0) {
|
||||
auto& presets = processorRef.presetManager.getPresets();
|
||||
int idx = 0;
|
||||
for (int i = 0; i < result - 1 && i < static_cast<int>(presets.size()); ++i)
|
||||
++idx;
|
||||
if (idx < static_cast<int>(presets.size()))
|
||||
processorRef.presetManager.applyPreset(presets[idx].name, processorRef.apvts);
|
||||
}
|
||||
});
|
||||
menu.showMenuAsync(juce::PopupMenu::Options().withTargetComponent(&presetButton),
|
||||
[this, menuOrder](int result) {
|
||||
if (result > 0 && result <= static_cast<int>(menuOrder.size())) {
|
||||
auto name = menuOrder[static_cast<size_t>(result) - 1];
|
||||
auto& presets = processorRef.presetManager.getPresets();
|
||||
for (int i = 0; i < static_cast<int>(presets.size()); ++i) {
|
||||
if (presets[i].name == name) {
|
||||
currentPresetIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
applyCurrentPreset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MainContentComponent::applyCurrentPreset() {
|
||||
auto& presets = processorRef.presetManager.getPresets();
|
||||
if (presets.empty()) return;
|
||||
currentPresetIndex = juce::jlimit(0, static_cast<int>(presets.size()) - 1, currentPresetIndex);
|
||||
processorRef.presetManager.applyPreset(presets[currentPresetIndex].name, processorRef.apvts);
|
||||
patchLCD.setText(presets[currentPresetIndex].name);
|
||||
}
|
||||
|
||||
void MainContentComponent::paint(juce::Graphics& g) {
|
||||
|
|
@ -682,10 +816,6 @@ void MainContentComponent::resized() {
|
|||
delayTimeKnob.setBounds(1110, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delayFbKnob.setBounds(1198, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delayMixKnob.setBounds(1286, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
// Row 2: PINGPONG combo, SPREAD knob
|
||||
int delayRow2Y = fx2KnobY + lfoKnobSize + 4;
|
||||
delayPingPongBox.setBounds(1110, delayRow2Y, 100, 36);
|
||||
delaySpreadKnob.setBounds(1220, delayRow2Y, lfoKnobSize, lfoKnobSize);
|
||||
|
||||
// Reverb sub-section (X=1375, Y=690, W=265, H=130)
|
||||
reverbSizeKnob.setBounds(1383, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
|
|
@ -693,7 +823,10 @@ void MainContentComponent::resized() {
|
|||
reverbMixKnob.setBounds(1553, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
|
||||
// --- PRESET + SCALE ---
|
||||
presetButton.setBounds(20, 8, 120, 28);
|
||||
presetButton.setBounds(20, 8, 90, 28);
|
||||
patchLCD.setBounds(118, 8, 360, 28);
|
||||
prevPresetButton.setBounds(486, 8, 28, 28);
|
||||
nextPresetButton.setBounds(518, 8, 28, 28);
|
||||
scaleLabel.setBounds(1520, 10, 42, 20);
|
||||
uiScaleBox.setBounds(1566, 8, 80, 24);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue