Skip to content

Commit 46c429d

Browse files
committed
Squashed 'src/dmt/' changes from 9d799be..2410184
2410184 feat: implement window state management 856d7f1 fix: window resetting to default size when toggling header visibility in dynamic mode d3e28e9 feat: implement dynamic window resizing f2daabe fix: adjust to dmt api changes c7191d4 chore: fix merge conflicts 973f660 Merge commit '187e9f260d0bafca5e2177c79c6b9088c58a5475' 79325bf fix: resolve merge conflicts 58ebc84 Merge branch 'main' of https://github.com/Dimethoxy/Oscilloscope d735c62 settings: update display update notifications to use DMT_DISABLE_UPDATE_NOTIFICATION 7904580 oscilloscope: improve image handling for Windows compatibility be0ed3f oscilloscope: try out manual moving on Windows dfab8e1 oscilloscope: remove custom scrolling implementation 4ce1567 oscilloscope: add sanity checks to scrolling b8cec2d oscilloscope: improve left scrolling implementation 3ae87e3 oscilloscope: try to roll our own implementation of left-scrolling to avoid Direct2D crashing windows 83cb934 fix: adjust oscilloscope image section movement and enable clearing with transparency 0df88b4 fix: comment out backImage.clear to prevent unwanted clearing during rendering 41a613e fix: optimize oscilloscope rendering for improved performance and stability 45a7e70 fix: enable clearing of the oscilloscope image with a defined rectangle 7477622 debug: lobotomize the oscilloscope image scrolling to isolate a crash cause bae21b0 fix: track last repaint time for frame-independent timing in Alerts b277642 fix: implement destructors to properly remove listeners in GUI components 143a4db fix: make sure to properly stop timers in destructors b03ee2a fix: oscilloscope chunks not connected correctly 18157df fix: remove constexpr from AhdEnvelope constructor for compatibility a2f05c1 refactor: update image retrieval methods to use getFrontImage() for better clarity and performance a76e207 feat: replace float variables with atomic<float> for thread-safe access in Oscilloscope 65cdeaa oscilloscope: impleent double buffering and atomic renderer swapping 2b2a86d feat: add default constructors to MinMaxRenderer and OscilloscopeRenderer classes 88107ca feat: add default constructor to Properties class 47489c8 fix: add default constructors to DigitalOscillator, DigitalWaveform, and SynthSound classes 8645d49 fix: add JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR to multiple classes 17fdafd fix: not saving APVTS reference in OscilloscopeDisplay constructor 58045c6 fix: properly destruct OscilloscopeDisplay and remove parameter listeners c3183ea Merge commit '55a41f3cc17c645cb21b05451bf7bfcbb996e243' as 'src/dmt' 82430fe chore: update subproject commits for clap and melatonin_blur 27d3687 fix: typo 7fcbc41 fix: improve hidpi scale factor detection for the 9th million time a41c618 refactor: rename variable for clarity in getScaleFactor method 25e433a refactor: enhance scale factor retrieval logic in Scaleable class 30fd5f6 refactor: improve scale factor retrieval in Scaleable class 56cca8a Merge commit 'd0428f771302ceb755846be967daf7a221824710' as 'src/dmt' a66a94e chore: remove broken subtree d34b12f refactor: add settings overrides and replacements to AbstractPluginProcessor constructor cca7cad fix: correct dimensions for OscilloscopePanel in PluginEditor constructor a9ea1a7 refactor: remove bypass parameter handling from processBlock 3493fad fix: swap constructor parameters for OscilloscopePanel ba36f06 refactor: Update launch configurations and include paths for development environments 415422a refactor: Update to newest DMT version f14f3c7 Merge commit '66b73a42bca486c5620fdb1c2b8a906045be3b40' as 'src/dmt' 66b73a4 Squashed 'src/dmt/' content from commit 9e85611 bb6600a refactor: Update CMake configuration 01c4508 refactor: Update PluginProcessor to initialize properties with settings overrides and replacements 189cfbd refactor: Update PluginEditor to use mainLayout a99cfff fix: Resolve build errors 4a18964 refactor: Prepare repository for DMT 25.1.3 c989181 Update CMakeLists.txt b86a8b2 Add files via upload 71c83a9 Rename uninstall.ico to resources/uninstall.ico 7000ea6 Add files via upload 7f24ac4 Update README.md f2c70cd Update README.md 2e5f703 fix: Correct typo in background color reference in PluginEditor 0e44342 chore: Add .clang-format configuration ab5d968 refactor: Initialize properties in PluginProcessor b9ac047 chore: remove placeholder files and add installer scripts for macOS and Windows 9e7c2d6 Merge branch 'main' of https://github.com/Dimethoxy/Oscilloscope 5d4baaf refactor: OscilloscopeComponent e2b5536 Create CODE_OF_CONDUCT.md b447845 Update LICENSE.txt 6057b4a gui: Move Oscilloscope parameter handling logic from Panel to Component 1b20be0 chore: Copy Actions from Disflux ff34d2e fix: Add BUNDLE_ID to CMakeLists for oscilloscope plugin 28cbbed chore: Add package files 3612014 chore: Add CI workflow 20f1b05 feat: Add VersionManager a626192 fix: Update base dimensions in PluginEditor and refresh dmt subproject commit 1a48d66 chore: Remove cmake-multi-platform workflow file 9d12f85 Merge branch 'main' of https://github.com/Dimethoxy/Oscilloscope 3f94d75 chore: Update Project to use latest dmt version fd7593e Update CMakePresets.json c17b04d Create cmake-multi-platform.yml 4675f74 chore: Update JUCE dependency to version 8.0.1 d15d399 refactor: Renamed layout setting to window 4463707 chore: Add TOML library cd90f78 feat: Initialize configuration in PluginProcessor constructor 999a197 Still refactoring, going to sleep now f4dffeb Improved resizing 829d3e4 Experimenting with image caching e966266 Update scripts 59cb50a Kinda improved resizing 69fc920 More tracing b79002b Updated perfetto to custom fork 8b89397 Linux Perfetto build option ea700bd Update .gitignore 06e5d71 Test 4a2f7d9 Upgraded CPM and added OpenGL drawing 2a729b2 Fixed Linux issues b5d48d4 Linux build 821f847 Fixed build presets 97f7bf5 Parallel rendering implemented 6503c52 Perfetto kinda working 93bc8d0 Fix presets acdab0d Compiling with Ninja 7717641 Added Perfetto 4f5ac7f Small changes 08c5694 Fixed some warnings 3dd9ec6 More icons 39628da Added icon for zoom slider fc0ff00 Mostly refactoring f3a7132 Added CLAP build target eeb75fb Added Ableton debugging 0ada542 Merge branch 'main' of https://github.com/Lunix-420/Oscilloscope 435f2a8 Increase size of oscilloscopeFifo in PluginProcessor.cpp 9fc5559 Update README.md 005bf33 Update README.md 28325f1 Increase size of oscilloscopeFifo in PluginProcessor.cpp f41a633 It's working 8a09b9f Last for today a8704d4 Add locc fadf533 Increase size of oscilloscopeFifo in PluginProcessor.cpp 17f11c0 Update constructor c22c798 Update FifoAudioBuffer in PluginProcessor.h 1f0c8d1 Edited Build Settings f1567c3 Added Oscilloscope Panel to Editor 204898d chore: Update includes and dependencies e136c29 Add OscilloscopeComponent to PluginEditor 65a2702 Removed test code b1aaf1a Test for FifoAudioBuffer 8b17095 Removed OpenGLContext 91ebd10 Prepare for Rewrite b32e7cd Update includes 81c2e6d Switching to png rendering 254c270 Added OpenGL drawing 2f38004 Improved includes b617b87 Fixed RingBuffer writing 4f6dd35 Passed reference to OscilloscopeProcessor 329cfe7 Implemented OscilloscopePanel 7ccad06 Pushed files to library da30b64 Refactoring a4c9982 Update CMakeLists.txt and PluginEditor.cpp cac6132 Project compiles 6c42a4c Started implementing the buffer queue 5a7c214 Setup & Refactoring d117e29 Removed broken test feature ad4b0a5 Merge branch 'main' of https://github.com/Lunix-420/Oscilloscope 9f776fd Help 88d9d73 Delete readme..md a64fece Create README.md 1e8acf0 Improved build chain e74d85f Added build variants a23786e Added another test for RingBuffer 960429f Improved thread safety and error handling 1871414 More Docs 3b033a1 Documentation 7156a8b Update RingBuffer 6e987f7 Added GoogleBenchmarks and updated RingBufferTest 6d86309 Fixed Warnings 9028d02 Fixed CMake Project Name 256090e UnitTests are working 6fb2be0 Updated RingBuffer b919a50 Build chain setup 7b45b11 Init git-subtree-dir: src/dmt git-subtree-split: 2410184
1 parent 1044a14 commit 46c429d

42 files changed

Lines changed: 1489 additions & 156 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Dimethoxy Library
22

3+
<<<<<<< HEAD
4+
> [!WARNING]
5+
> This library is under active development and primarily intended for use within Dimethoxy projects \
6+
> External use is unsupported.
7+
8+
The Dimethoxy Library is the core engine behind all Dimethoxy plugins — a modular C++ codebase built for high-performance digital signal processing and plugin development.
9+
From custom DSP algorithms to GUI systems and utility wrappers, this library powers nearly every aspect of our audio tools.
10+
11+
## Features
12+
- 🚀 High-performance DSP tailored for aggressive electronic music (Hardstyle, Hardcore, Uptempo, etc.)
13+
- 🎛️ Modular components for efficient plugin development
14+
- 🧱 Shared utilities, math, parameter handling, and UI helpers
15+
- 🧪 Designed for maintainability and extensibility across multiple projects
16+
17+
## Used In
18+
- [Disflux](https://github.com/Dimethoxy/Disflux) — Transient Smearing Audio Plugin for Windows, MacOS and Linux
19+
- [Oscilloscope](https://github.com/Dimethoxy/Oscilloscope) — Work in Progress Oscilloscope Audio Plugin for Windows, MacOS and Linux
20+
21+
22+
## License
23+
This project is licensed under AGPLv3. \
24+
=======
325
> [!CAUTION]
426
> **Do NOT use this library in your own projects if you expect support.** <br>
527
> We will **not fix your issues, bugs, or integration problems** if you use it externally.
@@ -27,4 +49,5 @@ If it breaks, you own the pieces.
2749
## License
2850

2951
This project is licensed under AGPLv3.
52+
>>>>>>> a5e5c670fddd956080480f24e1397fa5872f9993
3053
Any project using **any** part of this library **must** also be licensed under AGPLv3 or a compatible open-source license.

‎app/AbstractPluginEditor.h‎

Lines changed: 219 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,100 @@ class AbstractPluginEditor
2323
using OpenGLContext = juce::OpenGLContext;
2424

2525
public:
26+
// Window sizing mode
27+
enum class WindowMode
28+
{
29+
Fixed,
30+
Dynamic
31+
};
32+
33+
// Configuration for window sizing behavior
34+
struct WindowConfig
35+
{
36+
int baseWidth;
37+
int baseHeight;
38+
WindowMode mode = WindowMode::Fixed;
39+
float minSizeMultiplier = 0.5f;
40+
float maxSizeMultiplier = 2.0f;
41+
float minAspectRatio = 0.5f;
42+
float maxAspectRatio = 2.0f;
43+
int dynamicStartWidth = -1;
44+
int dynamicStartHeight = -1;
45+
};
46+
47+
// Custom constrainer that enforces aspect ratio bounds (min/max aspect
48+
// ratios)
49+
class AspectRatioBoundsConstrainer : public juce::ComponentBoundsConstrainer
50+
{
51+
public:
52+
AspectRatioBoundsConstrainer(float minRatio,
53+
float maxRatio,
54+
int baseW,
55+
int baseH,
56+
int headerH)
57+
: minAspectRatio(minRatio)
58+
, maxAspectRatio(maxRatio)
59+
, baseWidth(baseW)
60+
, baseHeight(baseH)
61+
, headerHeight(headerH)
62+
{
63+
}
64+
65+
void checkBounds(juce::Rectangle<int>& bounds,
66+
const juce::Rectangle<int>& previousBounds,
67+
const juce::Rectangle<int>& limits,
68+
bool isStretchingTop,
69+
bool isStretchingLeft,
70+
bool isStretchingBottom,
71+
bool isStretchingRight) override
72+
{
73+
// First apply default size limits
74+
ComponentBoundsConstrainer::checkBounds(bounds,
75+
previousBounds,
76+
limits,
77+
isStretchingTop,
78+
isStretchingLeft,
79+
isStretchingBottom,
80+
isStretchingRight);
81+
82+
// Then enforce aspect ratio bounds
83+
int w = bounds.getWidth();
84+
int h = bounds.getHeight();
85+
double currentRatio = (double)w / (double)h;
86+
87+
if (currentRatio < minAspectRatio) {
88+
// Too narrow - increase width
89+
w = static_cast<int>(h * minAspectRatio);
90+
} else if (currentRatio > maxAspectRatio) {
91+
// Too wide - decrease width
92+
w = static_cast<int>(h * maxAspectRatio);
93+
}
94+
95+
bounds.setSize(w, h);
96+
}
97+
98+
private:
99+
float minAspectRatio, maxAspectRatio;
100+
int baseWidth, baseHeight, headerHeight;
101+
};
102+
26103
// Strategy function type for layout initialization
27104
using LayoutInitializer = std::function<void(dmt::gui::window::Layout&)>;
28105

106+
// Primary constructor with WindowConfig
29107
AbstractPluginEditor(dmt::app::AbstractPluginProcessor& _p,
30108
juce::String _name,
31-
int _baseWidth,
32-
int _baseHeight,
109+
const WindowConfig& _windowConfig,
33110
LayoutInitializer&& _layoutInit)
34111
: juce::AudioProcessorEditor(&_p)
35112
, p(_p)
36-
, baseWidth(_baseWidth)
37-
, baseHeight(_baseHeight)
113+
, baseWidth(_windowConfig.baseWidth)
114+
, baseHeight(_windowConfig.baseHeight)
115+
, windowMode(_windowConfig.mode)
116+
, minSizeMultiplier(_windowConfig.minSizeMultiplier)
117+
, maxSizeMultiplier(_windowConfig.maxSizeMultiplier)
118+
, minAspectRatio(_windowConfig.minAspectRatio)
119+
, maxAspectRatio(_windowConfig.maxAspectRatio)
38120
, sizeFactor(p.sizeFactor)
39121
, mainLayout({}, {})
40122
, compositor(_name, mainLayout, p.apvts, p.properties, sizeFactor)
@@ -81,11 +163,46 @@ class AbstractPluginEditor
81163

82164
#endif
83165

84-
setConstraints(baseWidth, baseHeight + headerHeight);
166+
// Configure window constraints based on mode
167+
if (windowMode == WindowMode::Dynamic) {
168+
// Create custom constrainer for aspect ratio bounds
169+
aspectRatioBoundsConstrainer =
170+
std::make_unique<AspectRatioBoundsConstrainer>(
171+
minAspectRatio,
172+
maxAspectRatio,
173+
baseWidth,
174+
baseHeight,
175+
static_cast<int>(dmt::Settings::Header::height));
176+
setConstrainer(aspectRatioBoundsConstrainer.get());
177+
activateDynamicWindowSize(minSizeMultiplier, maxSizeMultiplier);
178+
} else {
179+
setConstraints(baseWidth, baseHeight + headerHeight);
180+
}
85181
setResizable(false, true);
86182

87-
const auto startWidth = baseWidth * sizeFactor;
88-
const auto startHeight = (baseHeight + headerHeight) * sizeFactor;
183+
// Resolve initial window size using priority order
184+
int initialWidth = baseWidth;
185+
int initialHeight = baseHeight + headerHeight;
186+
187+
// Priority 1: Check for saved window state
188+
int savedWidth = p.getSavedWindowWidth();
189+
int savedHeight = p.getSavedWindowHeight();
190+
if (savedWidth > 0 && savedHeight > 0) {
191+
initialWidth = savedWidth;
192+
initialHeight = savedHeight;
193+
isHeaderHidden = p.getSavedHeaderHiddenState();
194+
}
195+
// Priority 2: Use dynamic start dimensions if in dynamic mode
196+
else if (windowMode == WindowMode::Dynamic &&
197+
_windowConfig.dynamicStartWidth > 0 &&
198+
_windowConfig.dynamicStartHeight > 0) {
199+
initialWidth = _windowConfig.dynamicStartWidth;
200+
initialHeight = _windowConfig.dynamicStartHeight;
201+
}
202+
// Priority 3: Use base dimensions (default, already set above)
203+
204+
const auto startWidth = initialWidth * sizeFactor;
205+
const auto startHeight = initialHeight * sizeFactor;
89206
setSize(startWidth, startHeight);
90207

91208
// Set the callback for header visibility changes
@@ -94,7 +211,30 @@ class AbstractPluginEditor
94211
});
95212
}
96213

97-
~AbstractPluginEditor() override = default;
214+
// Deprecated: Use the WindowConfig constructor instead
215+
[[deprecated("Use constructor with WindowConfig parameter instead")]]
216+
AbstractPluginEditor(dmt::app::AbstractPluginProcessor& _p,
217+
juce::String _name,
218+
int _baseWidth,
219+
int _baseHeight,
220+
LayoutInitializer&& _layoutInit)
221+
: AbstractPluginEditor(_p,
222+
_name,
223+
WindowConfig{ _baseWidth, _baseHeight },
224+
std::forward<LayoutInitializer>(_layoutInit))
225+
{
226+
}
227+
228+
~AbstractPluginEditor()
229+
{
230+
// Ensure OpenGL context is detached before destruction
231+
if (openGLContext.isAttached()) {
232+
openGLContext.detach();
233+
}
234+
235+
// Stop the debounce timer if it's running
236+
stopTimer();
237+
}
98238

99239
//==============================================================================
100240
// JUCE overrides
@@ -144,11 +284,6 @@ class AbstractPluginEditor
144284
detachCompositorForResize();
145285
}
146286

147-
//==============================================================================
148-
// Handle peer creation for Windows Direct2D setup
149-
150-
void parentHierarchyChanged() override {}
151-
152287
//==============================================================================
153288
// JUCE overrides
154289

@@ -167,12 +302,51 @@ class AbstractPluginEditor
167302
}
168303
}
169304

305+
void activateDynamicWindowSize(float minRatio, float maxRatio)
306+
{
307+
if (auto* constrainer = this->getConstrainer()) {
308+
constrainer->setFixedAspectRatio(0.0); // Allow variable aspect ratio
309+
// Only limit height; width is controlled by aspect ratio bounds
310+
const auto minHeight = static_cast<int>(baseHeight * minRatio);
311+
const auto maxHeight = static_cast<int>(baseHeight * maxRatio);
312+
// Calculate width range based on aspect ratio bounds and height range
313+
const auto minWidth = static_cast<int>(minHeight * minAspectRatio);
314+
const auto maxWidth = static_cast<int>(maxHeight * maxAspectRatio);
315+
constrainer->setSizeLimits(minWidth, minHeight, maxWidth, maxHeight);
316+
} else {
317+
jassertfalse; // Constrainer not set
318+
}
319+
}
320+
170321
void handleHeaderVisibilityChange(bool isHeaderVisible)
171322
{
323+
isHeaderHidden = !isHeaderVisible;
172324
const int adjustedHeight =
173325
isHeaderVisible ? baseHeight + headerHeight : baseHeight;
174-
setConstraints(baseWidth, adjustedHeight);
175-
setSize(baseWidth * sizeFactor, adjustedHeight * sizeFactor);
326+
327+
if (windowMode == WindowMode::Dynamic) {
328+
// In dynamic mode, recalculate size limits but keep aspect ratio bounds
329+
if (auto* constrainer = this->getConstrainer()) {
330+
const auto minHeight =
331+
static_cast<int>(adjustedHeight * minSizeMultiplier);
332+
const auto maxHeight =
333+
static_cast<int>(adjustedHeight * maxSizeMultiplier);
334+
const auto minWidth = static_cast<int>(minHeight * minAspectRatio);
335+
const auto maxWidth = static_cast<int>(maxHeight * maxAspectRatio);
336+
constrainer->setSizeLimits(minWidth, minHeight, maxWidth, maxHeight);
337+
}
338+
// Keep width the same, only adjust height by header height
339+
int heightAdjustment = static_cast<int>(
340+
(isHeaderVisible ? headerHeight : -headerHeight) * sizeFactor);
341+
setSize(getWidth(), getHeight() + heightAdjustment);
342+
} else {
343+
// In fixed mode, enforce fixed aspect ratio
344+
setConstraints(baseWidth, adjustedHeight);
345+
setSize(baseWidth * sizeFactor, adjustedHeight * sizeFactor);
346+
}
347+
348+
// Save the header visibility state
349+
p.saveWindowState(getWidth(), getHeight(), isHeaderHidden);
176350
}
177351

178352
dmt::gui::window::Layout& getMainLayout() { return mainLayout; }
@@ -209,30 +383,36 @@ class AbstractPluginEditor
209383
void attachCompositorAfterResize()
210384
{
211385
if (!compositorAttached) {
212-
// Snap to the correct aspect ratio, considering header visibility
213-
auto bounds = getLocalBounds();
214-
bool headerVisible = compositor.isHeaderVisible();
215-
int aspectHeight =
216-
headerVisible ? (baseHeight + headerHeight) : baseHeight;
217-
const double aspect = (double)baseWidth / (double)aspectHeight;
218-
int w = bounds.getWidth();
219-
int h = bounds.getHeight();
220-
double currentAspect = (double)w / (double)h;
221-
222-
if (currentAspect > aspect) {
223-
// Too wide, adjust width
224-
w = static_cast<int>(h * aspect);
225-
} else if (currentAspect < aspect) {
226-
// Too tall, adjust height
227-
h = static_cast<int>(w / aspect);
386+
// For fixed mode, snap to the correct aspect ratio
387+
// For dynamic mode, keep the current size as-is
388+
if (windowMode == WindowMode::Fixed) {
389+
auto bounds = getLocalBounds();
390+
bool headerVisible = compositor.isHeaderVisible();
391+
int aspectHeight =
392+
headerVisible ? (baseHeight + headerHeight) : baseHeight;
393+
const double aspect = (double)baseWidth / (double)aspectHeight;
394+
int w = bounds.getWidth();
395+
int h = bounds.getHeight();
396+
double currentAspect = (double)w / (double)h;
397+
398+
if (currentAspect > aspect) {
399+
// Too wide, adjust width
400+
w = static_cast<int>(h * aspect);
401+
} else if (currentAspect < aspect) {
402+
// Too tall, adjust height
403+
h = static_cast<int>(w / aspect);
404+
}
405+
setSize(w, h);
228406
}
229-
setSize(w, h);
230407

231408
// Set compositor bounds to fill the editor
232409
addAndMakeVisible(compositor);
233410
compositor.setBounds(getLocalBounds());
234411
compositorAttached = true;
235412
repaint();
413+
414+
// Save the current window state after size correction
415+
p.saveWindowState(getWidth(), getHeight(), isHeaderHidden);
236416
}
237417
}
238418

@@ -324,6 +504,12 @@ class AbstractPluginEditor
324504
const int& headerHeight = dmt::Settings::Header::height;
325505
const int baseWidth;
326506
const int baseHeight;
507+
WindowMode windowMode = WindowMode::Fixed;
508+
float minSizeMultiplier = 0.5f;
509+
float maxSizeMultiplier = 2.0f;
510+
float minAspectRatio = 0.5f;
511+
float maxAspectRatio = 2.0f;
512+
bool isHeaderHidden = false;
327513
int lastWidth = baseWidth;
328514
int lastHeight = baseHeight;
329515
double ratio = baseWidth / baseHeight;
@@ -340,6 +526,7 @@ class AbstractPluginEditor
340526
dmt::gui::window::Compositor compositor;
341527

342528
OpenGLContext openGLContext;
529+
std::unique_ptr<AspectRatioBoundsConstrainer> aspectRatioBoundsConstrainer;
343530
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AbstractPluginEditor)
344531
};
345532
} // namespace app

0 commit comments

Comments
 (0)