Skip to content

Conversation

@jerryuhoo
Copy link
Owner

Fixed a lot of bugs from v1.5.0b like #89 and other thread issues

Introduces a function to determine if modulation is active and uses it to conditionally execute LFO processing. This optimization avoids unnecessary computations when no modulation routings are active, improving performance.
Replaces the high-CPU waterfall effect with direct spectrum rendering, enhancing performance and reducing resource usage.

Introduces thread-safe updates for spectrum data via AsyncUpdater, ensuring accurate and efficient audio-thread communication.

Refines smoothing logic for spectrum data with adjustable attack and release coefficients.

Restores peak-hold behavior and optimizes memory usage by eliminating unnecessary image manipulations.

Updates API usage in related components to align with the new rendering logic.
Replaces asynchronous updates with a timer for smoother rendering at a fixed frame rate.

Adds temporal and spatial smoothing for cleaner visuals, improving performance and reducing CPU usage.

Fixes peak resetting logic and enhances mouse interaction reliability.
Introduces logic for decaying the peak line towards zero when the mouse is not hovering over the spectrogram component, enhancing the visual presentation.

Improves reliability of mouse-over detection by updating the state within the paint method. Peak-hold data now only updates when the mouse is over the component, preventing unnecessary updates.

Ensures peak-hold reset on mouse enter for a fresh session and maintains decay animation visibility even when the mouse leaves.
Replaces direct drawing in the paint method with a pre-rendered cached image to improve rendering efficiency. Moves painting logic to a new method for generating the background image. Introduces a sparse array for frequency labels to reduce redundant computations.
Refactors LFO shape update logic to use atomic flags for efficiency, enabling selective updates. Adds mechanisms to notify and refresh UI components when LFO shapes change, improving synchronization between DSP and UI layers.

Introduces a reset mechanism in the audio processor to handle state resets safely. Enhances preset handling by clearing modulation routings and resetting parameters to defaults.
Improves performance by skipping LFO updates when no modulation is active, reducing unnecessary computations. Adds support for responding to preset changes by introducing a listener and updating animation status accordingly.

Enhances maintainability by centralizing logic for checking modulation status into a helper function. Updates painting logic to conditionally render LFO curves only when animation is active.
Adds support for detecting changes in display scale factor to optimize rendering. Updates cached background images only when the scale changes, reducing unnecessary redraws. Adjusts painting logic to leverage the pre-rendered cache for efficiency. Simplifies layout updates and ensures proper scaling of UI elements.

Refines spectrum update logic for clarity and performance, including adjustments to repaint calls and panel focus handling.
Introduces a caching system for pre-rendering static components of the drive slider, reducing redundant drawing operations and improving rendering performance.

Dynamic elements are separated from static ones to minimize
expensive computations, ensuring efficient frame updates.
Refactors the distortion processing loop to reuse the waveshaper function, reducing redundant calls to mode-dependent logic. Improves efficiency and ensures the waveshaper is determined once per processing block, not per sample.
Replaces manual Lissajous graph rendering with a cached image for improved performance and visual effects. Implements a transparent fade-out mechanism for smoother transitions. Refines pixel manipulation and coordinate transformations for better accuracy. Updates resizing logic to recreate the cached image when dimensions change.
Refines layout calculations for filter controls, buttons, and sliders by introducing a flexible grid system with better spacing and proportions.
Adds mouse wheel interaction for draggable buttons to adjust Q parameters, improving usability and precision. Introduces a callback mechanism for handling changes and ensures value clamping for stability. Refactors code formatting for consistency.

(cherry picked from commit 9e39c78)
(cherry picked from commit 101dbba6decda24849041593da98b0ebda3dfb11)
Replaces individual distortion mode components and attachments with arrays, reducing redundancy and improving scalability.

Simplifies visibility logic by consolidating it into a single function. Enhances maintainability by iterating over distortion modes in loops instead of duplicating logic.
This commit fixes a critical regression introduced in commit b53ad68, where adding or deleting multiband crossovers would cause audio parameters and LFO modulation routings to become desynchronized.

The regression was caused by a refactoring that changed how audio parameters are identified. The `Multiband` component's parameter-copying logic was still attempting to match parameters by display name (e.g., "Drive1"), which no longer existed, instead of their unique parameter IDs (e.g., "drive1"). This completely broke the state-shifting mechanism. Additionally, LFO modulation targets were not being updated at all during these operations.

This has been resolved by implementing a robust, data-driven state management system that correctly handles all band-related parameters and their LFO modulations.

Key Changes:

- **Fix Parameter Copying in `Multiband`:**
  - The parameter copying logic (`setParametersToAFromB`) has been refactored to use unique parameter IDs instead of display names, restoring the essential functionality of shifting audio parameters correctly.

- **Centralize Band Parameter Definitions:**
  - A new static function, `ParameterIDAndName::getBandParameterInfo()`, has been created in `Parameters.h` to serve as a single source of truth for all per-band parameter ID bases.
  - `Multiband.cpp` now uses this function to build its internal parameter lists, eliminating hardcoded values and improving maintainability.

- **Implement Robust LFO State Shifting:**
  - New methods (`shiftLfoModulationTargets`, `clearLfoModulationForBand`) have been added to `FireAudioProcessor` to reliably update modulation target IDs when bands are added or removed.
  - This logic is driven by the new centralized parameter list, making it robust and independent of specific ID string formats.

- **Refactor Preset Loading Logic:**
  - The complex and brittle sorting logic in `Preset.cpp` has been removed. Preset loading is now a simple, generic loop that correctly restores all parameter values from the XML, ensuring stability and backward compatibility with older presets.
…orting from reset()

This commit fixes an audio regression bug that was inadvertently introduced while fixing the state management for dynamic bands (in commit 8b3d89c). While the previous commit correctly fixed how parameters and LFOs are shifted, it caused audio regression tests to fail due to an unexpected change in the DSP processing.

The root cause was a block of parameter sorting logic that was incorrectly added to the `FireAudioProcessor::reset()` function. This logic was a flawed re-implementation of the sorting mechanism that had been correctly removed from `Preset.cpp`.

This was problematic for two reasons:
1.  **Incorrect Logic:** The new `std::sort` only sorted by frequency value and did not correctly handle the active/inactive `lineState`, unlike the original, more complex sorting logic it replaced.
2.  **Incorrect Location:** The `reset()` function's purpose is to reset the internal state of DSP modules, not to modify and re-order user-facing parameters. Placing this logic here caused the plugin's crossover frequencies to change unexpectedly during initialization (e.g., in `prepareToPlay`), leading to a different audio output.

The fix is to completely remove this erroneous sorting code from the `FireAudioProcessor::reset()` function. This restores the function to its simple and correct responsibility. The logic for sorting and interpreting active crossover frequencies remains properly handled by the `Multiband` component and the audio processing loop.

With this change, audio output is now consistent with previous versions, allowing regression tests to pass, while preserving all the recent fixes to state and LFO management.
Introduces `resortAndRedrawLines` in the Multiband class to encapsulate sorting and UI refresh logic. Replaces repetitive calls to individual update methods with this new method across the PluginEditor.

Removes the redundant `setMultiband` method for improved clarity and maintainability.
Introduces a reusable `ValuePopup` component for displaying slider values dynamically and enhances `ModulatableSlider` with drag callbacks (`onDragStart`, `onDragMove`, `onDragEnd`). Updates `BandPanel` and `GlobalPanel` to assign these callbacks, enabling interactive value feedback. Adjusts plugin editor to manage and position the popup based on slider behavior. Simplifies value display logic, including modulation-aware extreme values.
Introduces thread-safe methods for setting and clearing LFO data, improving data integrity during audio processing. Refactors LFOEditor to operate on copied LFO data, enabling safer data manipulation and UI updates. Enhances callback mechanism to propagate changes from UI to LFOManager seamlessly.

Fixes issues with state preservation during preset loading and automation by ensuring proper synchronization between UI and backend. Simplifies LFO reset logic and improves maintainability by centralizing state-clearing operations.
This commit improves the user experience by differentiating slider interactions and providing more intuitive visual feedback when adjusting the Drive parameter.

Previously, dragging a slider's main knob and its modulation handle triggered the same generic callbacks, leading to undesired UI behavior. Additionally, the temporary graph switch for the Drive knob would confusingly change the main panel's selected tab.

These changes address both issues:

- **Differentiated Drag Callbacks in `ModulatableSlider`**
  - Replaced the generic `onDrag...` callbacks with two distinct sets: `onMainDrag...` for the main knob and `onModDrag...` for the modulation handle.
  - Updated `mouseDown`, `mouseDrag`, and `mouseUp` to invoke the correct callback based on the interaction target.

- **Precise Graph Switching for Drive Knob**
  - Added `setGraphVisibilityForDriveDrag()` to `BandPanel`, which temporarily shows the `DistortionGraph` without altering the main tab switch state.
  - A pointer now stores the previously visible graph to ensure it can be correctly restored after the drag ends.

- **Centralized Callback Logic in `PluginEditor`**
  - Removed callback injection from `BandPanel` and `GlobalPanel` constructors.
  - All slider callbacks are now assigned directly within the `PluginEditor`, allowing for specific behaviors per slider.
  - The Drive knob's `onMainDragStart` and `onMainDragEnd` events are now connected to the new graph visibility logic in `BandPanel`.
This commit introduces several improvements and bug fixes to the LfoEditor, enhancing its usability and correctness.

- **Deselect on click away:** Clicking on an empty area of the editor will now clear the current selection of points. This provides a more intuitive user experience for managing selected points.

- **Correct horizontal inversion:** The "Invert Horizontally" function has been fixed to properly handle the LFO shape's curvature and endpoints.
    - Curvature values are now reversed and negated to accurately reflect the mirrored shape.
    - The Y-values of the start and end points are now swapped to ensure the entire shape is inverted correctly.

- **Enable additive marquee selection:** Holding the Shift key while drag-selecting now adds points to the existing selection instead of replacing it. This allows for more complex selections to be made across multiple areas of the editor.
This commit addresses a critical thread-safety issue that resulted in `EXC_BAD_ACCESS` crashes, particularly when rapidly switching presets.

The root cause was a race condition between the main UI thread and the real-time audio thread. The main thread would deallocate or modify shared data structures (specifically `LfoData` and `modulationRoutings` within the `LfoManager`) while the audio thread was concurrently attempting to read them during its processing cycle. This led to dereferencing invalid pointers and memory corruption.

The solution implements a robust locking strategy to ensure atomic access to all shared resources between the two threads:

- A central `juce::CriticalSection` (`dataAccessLock`) has been introduced in `LfoManager` to govern access to all shared LFO and modulation routing data.

- **Write operations on the main thread are now locked.** This includes the entire data-loading section within `state::loadStateFromXml`, as well as all methods in `LfoManager` that modify `lfoData` or `modulationRoutings` (e.g., `assignLfoToTarget`, `clearAllLfoData`).

- **Read operations on the audio thread are now locked.** The lock is acquired at the beginning of critical audio-thread functions that access the shared data, including `LfoManager::processBlock` and `FireAudioProcessor::getModulationInfoForParameter`, protecting them for their entire scope.

This ensures that the audio thread always operates on a consistent and valid set of data, completely resolving the threading conflict and stabilizing the plugin.
The standard way to do this in JUCE is with juce::MessageManager::callAsync
This commit resolves a critical bug in the preset saving mechanism where the state from a previously saved preset could bleed into a newly saved one.

The root cause was that the temporary `juce::XmlElement` (`presetXmlSingle`) used to build the preset file was not being fully cleared before each save operation. While `removeAllAttributes()` was called, `deleteAllChildElements()` was missing. This left stale child elements, such as `<LFO_STATE>` and `<MODULATION_STATE>`, in memory from the previous save.

This led to two primary issues:
1.  **Incorrect State Inheritance:** When saving a new preset (e.g., an "Init" patch), it would incorrectly inherit the LFO and modulation data from whichever preset was last saved in the session.
2.  **Failed Overwrites:** Attempting to overwrite an existing preset with modified LFO data would fail. The old, stale `<LFO_STATE>` child element would be written to the file alongside the new one, and upon loading, the plugin would read the old (first) element, ignoring the intended changes.

The fix involves two changes:
-   Added `presetXmlSingle.deleteAllChildElements()` in `StatePresets::savePreset` to ensure a clean slate before populating and saving the preset XML.
-   Removed a redundant and incorrect `file.replaceFileIn(file)` call within `writeXmlElementToFile`, as `xml.writeTo(file)` already handles file overwriting correctly.
@jerryuhoo jerryuhoo merged commit 570304d into master Oct 17, 2025
5 of 6 checks passed
@jerryuhoo jerryuhoo deleted the v1.5.0 branch October 17, 2025 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants