many UI fixes and new screenshot

This commit is contained in:
Armin 2026-09-17 11:29:01 +02:00
commit 88ed77a0c6
8 changed files with 43 additions and 29 deletions

View file

@ -36,14 +36,16 @@ if(NOT GIT_HASH)
endif() endif()
execute_process( execute_process(
COMMAND git rev-list --count HEAD COMMAND git status --porcelain
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_LOCAL_COMMITS OUTPUT_VARIABLE GIT_STATUS
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET ERROR_QUIET
) )
if(NOT GIT_LOCAL_COMMITS) if(GIT_STATUS STREQUAL "")
set(GIT_LOCAL_COMMITS "0") set(GIT_DIRTY "clean")
else()
set(GIT_DIRTY "dirty")
endif() endif()
string(TIMESTAMP BUILD_TIME "%Y-%m-%d %H:%M:%S") string(TIMESTAMP BUILD_TIME "%Y-%m-%d %H:%M:%S")

View file

@ -60,10 +60,10 @@ make install # build, codesign, un-quarantine, install to ~/Library/Audio/Plu
| Parameter | Range | Description | | Parameter | Range | Description |
|---|---|---| |---|---|---|
| Fade | 0.1 – 10 s | Trail persistence time | | Fade | 0.1 – 10 s | Trail persistence time |
| Sensitivity | -12 – +24 dB | Input gain for M/S decode | | Response | -12 – +24 dB | Input gain for M/S decode |
| Dot size | 0.5 – 8.0 px | Size of each plotted point | | Size | 0.5 – 8.0 px | Size of each plotted point |
| Smoothing | 0 – 0.99 | One-pole smoothing on M/S signals | | Smooth | 0 – 0.99 | One-pole smoothing on M/S signals |
| Afterglow | 0 – 5 s | Extra lingers of the trail after the fade time | | Glow | 0 – 5 s | Extra lingers of the trail after the fade time |
| Freeze | on/off | Stop capturing new points | | Freeze | on/off | Stop capturing new points |
## Project structure ## Project structure

View file

@ -47,7 +47,7 @@ TerionProcessorEditor::TerionProcessorEditor(TerionProcessor& p)
// Freeze button // Freeze button
addAndMakeVisible(freezeButton); addAndMakeVisible(freezeButton);
freezeButton.setButtonText("FRZ"); freezeButton.setButtonText("Freeze");
freezeButton.setColour(juce::ToggleButton::textColourId, juce::Colour(0xff888899)); freezeButton.setColour(juce::ToggleButton::textColourId, juce::Colour(0xff888899));
freezeButton.setColour(juce::ToggleButton::tickColourId, juce::Colour(0xff7ecfcf)); freezeButton.setColour(juce::ToggleButton::tickColourId, juce::Colour(0xff7ecfcf));
freezeAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ButtonAttachment>( freezeAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ButtonAttachment>(
@ -55,7 +55,7 @@ TerionProcessorEditor::TerionProcessorEditor(TerionProcessor& p)
// Mono button // Mono button
addAndMakeVisible(monoButton); addAndMakeVisible(monoButton);
monoButton.setButtonText("MONO"); monoButton.setButtonText("Mono");
monoButton.setColour(juce::ToggleButton::textColourId, juce::Colour(0xff888899)); monoButton.setColour(juce::ToggleButton::textColourId, juce::Colour(0xff888899));
monoButton.setColour(juce::ToggleButton::tickColourId, juce::Colour(0xff7ecfcf)); monoButton.setColour(juce::ToggleButton::tickColourId, juce::Colour(0xff7ecfcf));
monoAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ButtonAttachment>( monoAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ButtonAttachment>(
@ -133,10 +133,10 @@ void TerionProcessorEditor::paint(juce::Graphics& g)
g.setColour(juce::Colour(0xff555566)); g.setColour(juce::Colour(0xff555566));
g.setFont(juce::Font(juce::FontOptions(9.0f * s))); g.setFont(juce::Font(juce::FontOptions(9.0f * s)));
juce::String footerText = "Terion " juce::String footerText = "Terion (Git: "
+ juce::String(TERION_GIT_HASH) + juce::String(TERION_GIT_HASH)
+ " | " + juce::String(TERION_BUILD_TIME) + ", " + juce::String(TERION_GIT_DIRTY)
+ " | " + juce::String(TERION_GIT_LOCAL_COMMITS) + " local"; + ") | built: " + juce::String(TERION_BUILD_TIME);
g.drawFittedText(footerText, footer.reduced(static_cast<int>(10.0f * s), 0), g.drawFittedText(footerText, footer.reduced(static_cast<int>(10.0f * s), 0),
juce::Justification::centredLeft, 1); juce::Justification::centredLeft, 1);
@ -149,20 +149,18 @@ void TerionProcessorEditor::resized()
const int knobs = static_cast<int>(kKnobSize * s); const int knobs = static_cast<int>(kKnobSize * s);
const int margin = static_cast<int>(kKnobMargin * s); const int margin = static_cast<int>(kKnobMargin * s);
const int scaleW = static_cast<int>(76.0f * s); const int scaleW = static_cast<int>(76.0f * s);
const int monoW = static_cast<int>(72.0f * s); const int gap = static_cast<int>(8.0f * s);
auto bounds = getLocalBounds(); auto bounds = getLocalBounds();
auto footer = bounds.removeFromBottom(footerH); auto footer = bounds.removeFromBottom(footerH);
// Footer: scale dropdown + mono toggle on the right // Footer: scale dropdown with an 8px gap on the top and right
scaleBox.setBounds(footer.getWidth() - scaleW, 1, scaleW, footerH - 2); scaleBox.setBounds(footer.getWidth() - scaleW - gap, gap, scaleW, footerH - 2 - gap);
monoButton.setBounds(footer.getWidth() - scaleW - monoW, 1, monoW, footerH - 2);
// Scope fills the remaining area // Scope fills the remaining area
scope.setBounds(bounds); scope.setBounds(bounds);
// Controls layout: knob row sits 8px above the footer, FRZ stays top-right // Controls layout: knob row sits 8px above the footer, Freeze/Mono stay top-right
const int gap = static_cast<int>(8.0f * s);
const int numKnobs = 5; const int numKnobs = 5;
const int rowH = knobs + static_cast<int>(12.0f * s) - static_cast<int>(6.0f * s); const int rowH = knobs + static_cast<int>(12.0f * s) - static_cast<int>(6.0f * s);
const int startX = bounds.getWidth() - (numKnobs * (knobs + margin) + knobs + margin); const int startX = bounds.getWidth() - (numKnobs * (knobs + margin) + knobs + margin);
@ -173,8 +171,11 @@ void TerionProcessorEditor::resized()
dotSizeKnob.setBounds(startX + 2 * (knobs + margin), startY, knobs, knobs); dotSizeKnob.setBounds(startX + 2 * (knobs + margin), startY, knobs, knobs);
smoothingKnob.setBounds(startX + 3 * (knobs + margin), startY, knobs, knobs); smoothingKnob.setBounds(startX + 3 * (knobs + margin), startY, knobs, knobs);
afterglowKnob.setBounds(startX + 4 * (knobs + margin), startY, knobs, knobs); afterglowKnob.setBounds(startX + 4 * (knobs + margin), startY, knobs, knobs);
freezeButton.setBounds(bounds.getWidth() - knobs - margin, static_cast<int>(20.0f * s), const int freezeY = static_cast<int>(20.0f * s);
knobs, knobs - static_cast<int>(20.0f * s)); const int freezeH = knobs - static_cast<int>(20.0f * s);
freezeButton.setBounds(bounds.getWidth() - knobs - margin, freezeY, knobs, freezeH);
monoButton.setBounds(bounds.getWidth() - knobs - margin, freezeY + freezeH + gap,
knobs, freezeH);
const int labelY = startY + knobs - static_cast<int>(6.0f * s); const int labelY = startY + knobs - static_cast<int>(6.0f * s);
const int labelH = static_cast<int>(12.0f * s); const int labelH = static_cast<int>(12.0f * s);

View file

@ -8,22 +8,22 @@ TerionProcessor::TerionProcessor()
apvts(*this, nullptr, "Parameters", { apvts(*this, nullptr, "Parameters", {
std::make_unique<juce::AudioParameterFloat>( std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"fadeTime", 1}, juce::ParameterID{"fadeTime", 1},
"Fade Time", "Fade",
juce::NormalisableRange<float>(0.1f, 10.0f, 0.01f, 0.4f), juce::NormalisableRange<float>(0.1f, 10.0f, 0.01f, 0.4f),
2.0f), 2.0f),
std::make_unique<juce::AudioParameterFloat>( std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"sensitivity", 2}, juce::ParameterID{"sensitivity", 2},
"Sensitivity", "Response",
juce::NormalisableRange<float>(-12.0f, 24.0f, 0.1f, 0.5f), juce::NormalisableRange<float>(-12.0f, 24.0f, 0.1f, 0.5f),
0.0f), 0.0f),
std::make_unique<juce::AudioParameterFloat>( std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"dotSize", 3}, juce::ParameterID{"dotSize", 3},
"Dot Size", "Size",
juce::NormalisableRange<float>(0.5f, 8.0f, 0.1f, 0.5f), juce::NormalisableRange<float>(0.5f, 8.0f, 0.1f, 0.5f),
1.0f), 1.0f),
std::make_unique<juce::AudioParameterFloat>( std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"smoothing", 4}, juce::ParameterID{"smoothing", 4},
"Smoothing", "Smooth",
juce::NormalisableRange<float>(0.0f, 0.99f, 0.01f, 0.5f), juce::NormalisableRange<float>(0.0f, 0.99f, 0.01f, 0.5f),
0.0f), 0.0f),
std::make_unique<juce::AudioParameterBool>( std::make_unique<juce::AudioParameterBool>(
@ -36,7 +36,7 @@ TerionProcessor::TerionProcessor()
false), false),
std::make_unique<juce::AudioParameterFloat>( std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"afterglow", 7}, juce::ParameterID{"afterglow", 7},
"Afterglow", "Glow",
juce::NormalisableRange<float>(0.0f, 5.0f, 0.01f, 0.5f), juce::NormalisableRange<float>(0.0f, 5.0f, 0.01f, 0.5f),
0.5f) 0.5f)
}) })

View file

@ -13,6 +13,14 @@ VectorScope::~VectorScope()
void VectorScope::timerCallback() void VectorScope::timerCallback()
{ {
// Refresh the numeric readouts at kMetricsRateHz while keeping the trail
// animation running at the full 30 Hz.
const float frozen = processor.apvts.getRawParameterValue("freeze")->load();
if (frozen < 0.5f && ++metricFrame >= 30 / kMetricsRateHz)
{
metricFrame = 0;
cachedMetrics = processor.getMetrics();
}
repaint(); repaint();
} }
@ -183,7 +191,7 @@ void VectorScope::drawLegends(juce::Graphics& g, float cx, float cy, float radiu
void VectorScope::drawMetricsOverlay(juce::Graphics& g, const juce::Rectangle<int>& bounds) void VectorScope::drawMetricsOverlay(juce::Graphics& g, const juce::Rectangle<int>& bounds)
{ {
const auto& metrics = processor.getMetrics(); const auto& metrics = cachedMetrics;
const float s = currentScale; const float s = currentScale;
const int margin = static_cast<int>(10.0f * s); const int margin = static_cast<int>(10.0f * s);
const int lineHeight = static_cast<int>(14.0f * s); const int lineHeight = static_cast<int>(14.0f * s);
@ -217,6 +225,6 @@ void VectorScope::drawMetricsOverlay(juce::Graphics& g, const juce::Rectangle<in
drawMetric("Mid", metrics.midLevel, "dB", y); y += lineHeight; drawMetric("Mid", metrics.midLevel, "dB", y); y += lineHeight;
drawMetric("Side", metrics.sideLevel, "dB", y); y += lineHeight; drawMetric("Side", metrics.sideLevel, "dB", y); y += lineHeight;
drawMetric("Balance", metrics.balance, "dB", y); y += lineHeight; drawMetric("Balance", metrics.balance, "dB", y); y += lineHeight;
drawMetric("Phase", metrics.phaseAngle, "\u00B0", y); y += lineHeight; drawMetric("Phase", metrics.phaseAngle, "deg", y); y += lineHeight;
drawMetric("Crest", metrics.crestFactor, "dB", y); y += lineHeight; drawMetric("Crest", metrics.crestFactor, "dB", y); y += lineHeight;
} }

View file

@ -20,6 +20,9 @@ private:
TerionProcessor& processor; TerionProcessor& processor;
float currentScale = 1.0f; float currentScale = 1.0f;
juce::Image trail; juce::Image trail;
StereoMetrics cachedMetrics;
int metricFrame = 0;
static constexpr int kMetricsRateHz = 2;
void drawLegends(juce::Graphics& g, float cx, float cy, float radius); void drawLegends(juce::Graphics& g, float cx, float cy, float radius);
void drawHairCross(juce::Graphics& g, float cx, float cy); void drawHairCross(juce::Graphics& g, float cx, float cy);

View file

@ -1,5 +1,5 @@
#pragma once #pragma once
#define TERION_GIT_HASH "@GIT_HASH@" #define TERION_GIT_HASH "@GIT_HASH@"
#define TERION_GIT_DIRTY "@GIT_DIRTY@"
#define TERION_BUILD_TIME "@BUILD_TIME@" #define TERION_BUILD_TIME "@BUILD_TIME@"
#define TERION_GIT_LOCAL_COMMITS "@GIT_LOCAL_COMMITS@"

BIN
shot.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Before After
Before After