reSIDue DevLog

All notable changes to the reSIDue project will be documented in this file.

The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.

[v0.9.51] – 2025-10-13

Reference: Shared Wavetable Storage Architecture Fix
Summary: Fixed critical bug where voices 2 and 3 didn’t share wavetable data with voice 1, and enhanced table selector input behavior

Fixed

  • Voice 2/3 Wavetable Data Isolation: Fixed critical bug where wavetables applied to voices 2 or 3 sounded different from voice 1 due to each WavetablePlayer instance having separate empty storage
  • Voice Table Selector Immediate Updates: Fixed voice wavetable table selectors in WavetableSelectorComponent updating on every keystroke instead of only on Enter or focus loss
  • Filter/Vol Selector Immediate Updates: Fixed global Filter/Vol table selector updating on every keystroke preventing complete hex value entry

Changed

  • WavetablePlayer.h (lines 31-35): Added type aliases WavetableStorage and ControlFlagsStorage, changed constructor to take references to shared storage
  • WavetablePlayer.h (lines 86-95): Changed storage from per-instance arrays to references (allWavetables&, allWavetablesControl&) pointing to PluginProcessor-owned storage
  • WavetablePlayer.cpp (lines 23-31): Updated constructor to accept references and initialize reference members instead of creating per-instance storage
  • PluginProcessor.h (lines 212-214): Added sharedWavetables and sharedControlFlags members to own wavetable storage for all 3 voices within plugin instance
  • PluginProcessor.cpp (lines 34-41): Updated constructor initializer list to create all 3 wavetablePlayers and globalFilterVolPlayer with references to shared storage
  • WavetableSelectorComponent.cpp (lines 102-132, 183-214): Replaced onTextChange with onReturnKey and onFocusLost callbacks for deferred table switching

Technical Implementation

  • Shared Storage Pattern: PluginProcessor owns ONE copy of wavetable storage (sharedWavetables, sharedControlFlags) passed as references to all 3 WavetablePlayer instances
  • Per-Instance Isolation: Each plugin instance in DAW has its own wavetable storage preventing cross-track data contamination that would occur with static storage
  • Reference Member Architecture: WavetablePlayer uses reference members (WavetableStorage&, ControlFlagsStorage&) binding to PluginProcessor storage at construction time
  • 1D Array Simplification: Internal WavetablePlayer arrays remain 1D [regType] since each instance represents one voice, not all three voices
  • Deferred Input Updates: Table selector text fields buffer typed input until Enter or focus loss preventing incomplete hex value interpretation

Architecture Benefits

  • Consistent Voice Behavior: All 3 voices within plugin instance share same wavetable data pool ensuring voices 2 and 3 sound identical to voice 1
  • Multi-Instance Safety: Multiple plugin instances in DAW maintain separate wavetable storage preventing unintended cross-track data sharing
  • Memory Efficiency: Single shared storage pool per plugin instance eliminates data duplication across voices while maintaining proper isolation
  • Better UX: Users can type complete hex table numbers without plugin attempting to switch on partial values during typing

Root Cause Resolution

  • Per-Instance Empty Storage: Original architecture had 3 separate WavetablePlayer instances each with independent wavetable storage, but GUI only wrote to instance 0 leaving instances 1 and 2 with empty data
  • Text Callback Timing: WavetableSelectorComponent used onTextChange callback triggering table switches on every character typed instead of waiting for complete user input
  • Static Storage Consideration: Briefly considered static storage but rejected due to cross-instance contamination issue in multi-track DAW scenarios

Testing Recommendations

  • Load same wavetable on all 3 voices – verify they sound identical
  • Open multiple plugin instances in DAW – verify wavetable edits don’t affect other tracks
  • Type multi-digit hex values in table selectors – verify no intermediate table switching
  • Check that table switches occur only on Enter key or field focus loss

[v0.9.50] – 2025-10-12

Reference: Wavetable Index Input UX Improvements
Summary: Enhanced wavetable index field behavior for better user experience when entering table numbers manually

Fixed

  • Table Index Field Immediate Switching: Fixed wavetable table index editor switching tables on every keystroke – now only switches on Enter key press or field focus loss allowing complete hex value entry
  • Font Size Growth on Input: Fixed wavetable selector components increasing font size to 1.25x normal when typing in index fields – now maintains consistent font size across all operations

Changed

  • WavetableTableComponent.cpp (lines 109-135): Replaced onTextChange callback with onReturnKey and onFocusLost callbacks for deferred table switching
  • WavetableSelectorComponent.cpp (line 123): Changed font size from 1.25x multiplier to consistent C64FontForEditors() in onTextChange callback

Technical Implementation

  • Deferred Table Switching: Table index field now buffers typed input allowing multi-digit hex values without intermediate table changes
  • Event-Driven Updates: Table switching triggered by explicit user actions (Enter key, focus change) instead of every character input
  • Consistent Typography: All table selector font operations now use FontManager::getC64FontForEditors() eliminating visual size inconsistencies

Architecture Benefits

  • Better UX: Users can type complete hex values like “05A” without plugin attempting to switch to incomplete values like “0” then “05” then “05A”
  • Visual Consistency: Table selectors maintain uniform appearance across all user interactions and operations
  • Predictable Behavior: Table switching occurs at well-defined points matching standard text input conventions

[v0.9.49] – 2025-10-11

Reference: Advanced Sandbox Stability and Initialization Improvements
Summary: Enhanced singleton thread safety, state save/load validation, and embedded factory patch loading system for professional plugin initialization

Fixed

  • FontManager Singleton Initialization: Fixed potential crashes during plugin scanning by implementing lazy initialization with double-checked locking pattern instead of eager static initialization
  • State Save Memory Spikes: Added 20MB size validation to getStateInformation preventing watchdog timeouts from excessive memory allocations during project saves
  • State Load Memory Validation: Added comprehensive null pointer, size, and bounds checking to setStateInformation preventing crashes from corrupted or oversized state data
  • Missing Factory Patches Error: Fixed “File does not exist” error on first launch by implementing three-tier fallback system loading from embedded binary data when user file missing
  • Font Loading Error Reporting: Replaced catch-all exception handler with specific exception types providing detailed logging of font loading failures

Added

  • Thread-Safe Font Loading: Implemented std::mutex and std::atomic with acquire/release memory ordering for multi-instance plugin safety
  • State Truncation Marker System: Added ‘TRUC’ marker for oversized state data enabling graceful recovery when state exceeds size limits
  • Exception-Safe State Operations: Comprehensive try-catch blocks with bad_alloc handling ensuring plugin continues operating even if state operations fail
  • Embedded Factory Patches: Implemented BinaryData loading system automatically loading factory patches from embedded patches/patches.reSIDue when user file not found
  • Detailed State Logging: Added comprehensive logging of state save/load operations with byte counts and success/failure messages for debugging

Changed

  • FontManager.h (lines 27-135): Complete rewrite of singleton with lazy initialization, double-checked locking, thread safety, and specific exception handling with detailed logging
  • PluginProcessor.cpp (lines 270-320): Enhanced getStateInformation with size validation (20MB limit), truncation marker system, and comprehensive exception handling
  • PluginProcessor.cpp (lines 323-400): Enhanced setStateInformation with null checks, size validation, truncation marker detection, reserve-before-assign pattern, and graceful error handling
  • PatchManagerComponent.cpp (line 31): Added BinaryData.h include for embedded resource access
  • PatchManagerComponent.cpp (lines 548-613): Completely rewrote initializePatches() with three-tier fallback: user file → factory patches → default patches

Technical Implementation

  • Lazy Singleton Pattern: FontManager uses double-checked locking with atomic flag and mutex ensuring thread-safe initialization only when first accessed
  • Memory Ordering: std::atomic operations use acquire/release semantics preventing compiler/CPU reordering issues in multi-threaded environments
  • State Size Validation Flow: Check size → validate < 20MB → allocate with reserve → catch bad_alloc → log and degrade gracefully
  • Embedded Data Loading: BinaryData::getNamedResource("patches_reSIDue", dataSize) provides access to compiled-in factory patches from patches/patches.reSIDue
  • Three-Tier Fallback System: File exists check → load user file OR load embedded data OR create programmatic defaults with appropriate logging at each tier

Architecture Benefits

  • Plugin Scanner Safety: Lazy font loading prevents crashes during DAW plugin scanning phase when full initialization not needed
  • Memory Spike Prevention: 20MB limits prevent watchdog timeouts during project save operations in restrictive sandbox environments
  • Professional First Launch: Users see complete factory patch library instead of errors or minimal defaults improving initial experience
  • Multi-Instance Support: Thread-safe singleton allows multiple plugin instances to safely load fonts simultaneously without race conditions
  • Graceful State Recovery: Plugin continues operating even when state save/load fails, maintaining stability under all error conditions

Root Cause Resolution

  • Static Initialization Order Fiasco: FontManager eagerly loaded font during static initialization causing crashes when binary data not yet available during plugin scanning
  • Unbounded Memory Allocation: State save/load performed unbounded vector allocations potentially triggering sandbox watchdog timers with large projects
  • Missing Factory Patches: Plugin logged errors and created minimal defaults on first run instead of loading comprehensive factory patch library from embedded resources
  • Silent Font Failures: Catch-all exception handler masked specific font loading errors preventing diagnosis of sandbox permission issues

Testing Recommendations

  • Launch multiple plugin instances simultaneously – verify no font loading race conditions
  • Save/load large projects – verify no watchdog timeouts during state operations
  • Delete user patch file and launch – verify factory patches load without errors
  • Check logs for font loading success message and state operation byte counts
  • Test state restore with intentionally corrupted data – verify graceful continuation

[v0.9.48] – 2025-10-11

Reference: VST Sandbox Crash Prevention
Summary: Comprehensive fixes for VST host sandbox mode crashes including GUI thread safety, exception handling, memory management, and lifetime safety improvements

Fixed

  • GUI Thread Safety: Fixed AlertWindow operations being called from non-GUI threads causing Cocoa/Windows assertion failures in sandboxed environments
  • Exception Propagation: Replaced catch-all exception handlers with specific exception types preventing unhandled exception crashes
  • Memory Safety: Added bounds checking and error handling around const_cast operations preventing undefined behavior and segmentation faults
  • Use-After-Free: Fixed FileChooser lambda lifetime issues where component could be destroyed while file dialog was open
  • Unbounded Log Growth: Fixed FileLogger using unlimited size (0) which could exhaust disk space in long-running sessions
  • Memory Allocation Failures: Added guards against excessive memory allocation during binary serialization preventing watchdog timer crashes

Added

  • MessageManager::callAsync Wrapping: All AlertWindow operations now wrapped in MessageManager::callAsync ensuring GUI operations only happen on message thread
  • Exception Detail Logging: Added exception message capture and logging before displaying user alerts for better diagnostics
  • SafePointer Pattern: Implemented juce::Component::SafePointer in all FileChooser async lambdas with null checks preventing use-after-free
  • Memory Usage Validation: Added 5MB reserve with bad_alloc handling and 10MB absolute maximum size check for binary serialization
  • FileLogger Size Limit: Set 10MB maximum log file size with automatic rotation by JUCE framework
  • Try-Catch Protection: Added try-catch blocks around all const_cast operations with specific error logging

Changed

  • FileManager.cpp (lines 1426-1451): Split catch-all into specific std::exception handler and unknown exception handler with MessageManager::callAsync wrapping
  • PatchManagerComponent.cpp (lines 698-731): Applied same exception handling pattern to patch loading operations
  • PatchManagerComponent.cpp (lines 167-192, 212-242, 591-638, 659-678): Replaced raw this captures with SafePointer in all FileChooser lambdas
  • FileManager.cpp (lines 589-631): Added try-catch blocks around const_cast operations in wavetable loading with error logging and early return
  • FileManager.cpp (lines 645-676): Protected wavetable control flag loading with try-catch and error handling
  • LogManager.cpp (line 71): Changed FileLogger from unlimited size to 10MB limit with automatic rotation
  • FileManager.cpp (lines 141-178): Added memory allocation validation with reserve, bad_alloc handling, and size checking in appendData lambda

Technical Implementation

  • Thread Safety Pattern: juce::MessageManager::callAsync([message]() { AlertWindow::showMessageBoxAsync(...); }) ensures GUI operations on correct thread
  • Exception Handling Flow: Catch specific exception → log details → schedule async GUI alert → catch unknown → log generic → schedule async alert
  • SafePointer Pattern: [safeThis = juce::Component::SafePointer<T>(this)] with if (safeThis == nullptr) return; check at lambda entry
  • Memory Guard Flow: Reserve 5MB → catch bad_alloc → return empty. Then appendData checks size < 10MB before each insertion
  • const_cast Safety: try { auto& ref = const_cast<T&>(...); } catch (const std::exception& e) { log error; return false; }

Architecture Benefits

  • Sandbox Compatibility: Plugin now stable in restrictive VST sandbox environments (Logic Pro, Ableton, etc.)
  • Crash Prevention: Eliminated primary crash vectors: GUI thread violations, unhandled exceptions, memory errors, use-after-free
  • Resource Limits: Prevents unbounded resource consumption (logs, memory) that could trigger watchdog timers
  • Graceful Degradation: All error paths log diagnostic information and fail gracefully instead of crashing
  • Thread Safety: Proper separation between audio thread, message thread, and async file operations

Root Cause Resolution

  • GUI Thread Violations: Direct AlertWindow calls from file I/O contexts could execute on audio thread in sandboxed environments
  • Exception Masking: Catch-all handlers silently swallowed exceptions preventing proper error diagnosis and recovery
  • Lifetime Issues: Raw this captures in async lambdas created dangling pointers when components destroyed during file operations
  • Memory Spikes: Unbounded allocations during binary serialization could trigger sandbox watchdog timers
  • Log Accumulation: Unlimited log growth in long sessions could exhaust disk quotas in sandboxed file systems

Testing Recommendations

  • Load/save patches while audio playing – verify no dropouts
  • Close plugin GUI while file dialog open – verify no crash
  • Trigger errors during file operations – verify graceful handling
  • Long session with heavy logging – verify log rotation works
  • Low memory conditions – verify graceful degradation
  • Sandbox mode in Logic Pro / Ableton – verify stable operation

[v0.9.47] – 2025-10-11

Reference: EXEC Jump Destination Row Processing
Summary: Implemented second-pass EXEC parsing after EXEC=0x00/0x01 jump commands to ensure jump destination rows are fully processed

Fixed

  • Jump Destination Row Skipped: Fixed critical bug where EXEC=0x00 and EXEC=0x01 jump commands would update row pointer but immediately return without processing the destination row
  • Missing Register Updates: Jump destination row register values (FREQ/PULS/CTRL/A/D/S/R) are now applied instead of being skipped
  • Unprocessed EXEC Commands: EXEC commands at jump destination rows are now parsed enabling chained jump operations
  • Loop Logic Bypass: Loop logic (LOOP=0 row pointer establishment and LOOP>0 counter operations) at jump destinations now executes properly

Added

  • Loop-Based EXEC Processing: Implemented do-while loop around EXEC command parsing allowing up to 2 parses (initial + 1 reparse after jump)
  • Jump Detection System: Added bool didJump flag to detect when EXEC=0x00/0x01 triggers a jump operation
  • Parse Counter: Added int execParseCount counter limiting EXEC parsing to maximum 2 iterations preventing infinite loops
  • Chained Jump Support: One level of jump chaining now supported – jump destination can itself contain EXEC jump command

Changed

  • WavetablePlayer.cpp (lines 128-183): Wrapped EXEC processing in loop that continues while didJump && execParseCount < 2
  • Jump Return Logic: Replaced immediate return statements after jump with didJump = true; execParseCount++; continue; pattern
  • Processing Flow: After loop exits, normal register processing continues with final resolved row ensuring complete row execution

Technical Implementation

  • EXEC=0x00 Jump Flow: Updates state.currentRow, sets flags, continues loop to reparse EXEC at new row if execParseCount < 2
  • EXEC=0x01 Conditional Jump: Same flow as 0x00 but only when noteOn && loop.has_value() conditions met
  • Loop Termination: while (didJump && execParseCount < 2) ensures maximum 2 EXEC parses preventing endless loop scenarios
  • Complete Row Processing: Register values, remaining EXEC commands (0x02/0x03), loop logic, and row advancement all execute after final EXEC resolution

Architecture Benefits

  • Jump Destination Execution: Jump destination rows now fully processed including all register updates and control logic
  • Chained Operations: Enables complex wavetable sequences with jump-to-jump patterns within safety limits
  • Infinite Loop Prevention: Hard limit of 2 EXEC parses provides safety against circular jump references
  • Maintained State Consistency: All wavetable state operations (loop counters, row pointers) now execute correctly at jump destinations

Root Cause Resolution

  • Early Return Problem: Previous implementation used return immediately after updating state.currentRow in lines 140 and 155
  • Incomplete Execution: Early return skipped register processing (lines 176-193), loop logic (lines 195-233), and row advancement (lines 235-254)
  • Single-Pass Limitation: EXEC commands were only parsed once per processRegisterType() call, ignoring destination row EXEC values

[v0.9.46] – 2025-10-10

Reference: File Picker Path Respect Fix
Summary: Fixed binary and JSON save/load operations to respect user-selected file paths from file picker dialogs instead of redirecting to hardcoded directories

Fixed

  • File Picker Path Ignored: Fixed critical bug where file picker dialog selections were ignored and files were always saved to ~/.config/reSIDue directory
  • Binary Save Path Respect: Binary .reSIDue files now save to user-selected directory from file chooser instead of being redirected to hardcoded location
  • JSON Export Path Respect: JSON export now respects user-selected path from “Export to JSON” file chooser dialog
  • Load Path Handling: File loading now accepts full paths from file chooser enabling loading from any user-selected location

Changed

  • PatchManagerComponent.cpp (lines 183, 223, 653): Changed from passing filename-only to passing full path using selectedFile.getFullPathName()
  • FileManager::savePatchSetBinary(): Now accepts full filepath parameter and uses JUCE File class to handle complete path directly
  • FileManager::loadPatchSetBinary(): Now accepts full filepath parameter enabling loading from user-selected locations
  • FileManager::savePatchSetToFileV2(): Renamed parameter from overrideFilename to overrideFilepath and implemented full path handling with parent directory creation

Technical Implementation

  • FileManager.cpp Binary Save: Removed hardcoded ~/.config/reSIDue reconstruction, now uses provided path with JUCE File class for cross-platform compatibility
  • FileManager.cpp Binary Load: Eliminated directory path reconstruction logic, uses full path from file chooser directly
  • FileManager.cpp JSON Export: Added path handling branch – uses full path when provided, falls back to ~/.config/reSIDue/patches.json for automatic saves
  • Parent Directory Creation: Enhanced file save operations to create parent directories if they don’t exist for user-selected paths
  • Extension Handling: Automatic .reSIDue and .json extension enforcement using JUCE hasFileExtension() and withFileExtension() methods

Architecture Benefits

  • User Control Restored: Users can now save/load patch files to any location they choose respecting standard OS file picker behavior
  • Cross-Platform Path Handling: JUCE File class ensures proper path handling across Windows, macOS, and Linux
  • Backward Compatibility: Default automatic save/load operations still use ~/.config/reSIDue when no explicit path provided
  • Consistent Behavior: File operations now behave as users expect – saved files appear where file picker dialog specified

Root Cause Resolution

  • Filename Extraction Bug: Previous implementation extracted only filename from file chooser then reconstructed path to hardcoded directory
  • Path Reconstruction Logic: Old code used getFileNameWithoutExtension() losing user’s directory selection and forcing saves to fixed location
  • Missing Full Path Usage: File chooser returned complete path via getFullPathName() but code discarded directory information

[v0.9.45] – 2025-10-08

Reference: Relative Note Scaling System Implementation
Summary: Added EXEC command-controlled relative note scaling system for precise fractional semitone frequency modulation

Added

  • Relative Note Scaling Mode: New relativeNoteScaling boolean flag in WavetableState structure controlling frequency scaling behavior
  • EXEC 0x02 Command: Enable relative note scaling mode for current wavetable
  • EXEC 0x03 Command: Disable relative note scaling mode for current wavetable
  • Fractional Semitone Scaling: FrqR values now scale frequency by 1/32768th of a semitone when scaling mode is enabled
  • Filter/Vol Initialization: Enhanced initializeStates() to include Filter/Vol wavetable (index 5) in state initialization

Enhanced

  • Dual FrqR Processing Modes: FrqR column now supports both absolute frequency offset (default) and relative note scaling (when enabled)
  • Mathematical Precision: Relative scaling uses 2^(1/(12*32768)) ratio raised to FrqR value for accurate fractional semitone adjustment
  • State Parameter Integration: Added WavetableState parameter to processFreqStep() method for access to scaling mode flag
  • Complete State Reset: initializeStates() now properly resets relativeNoteScaling to false for all 6 wavetable types

Technical Implementation

  • Wavetable.h:79: Added bool relativeNoteScaling = false to WavetableState structure with default initialization
  • WavetablePlayer.cpp:159-163: EXEC 0x02 command sets state.relativeNoteScaling = true
  • WavetablePlayer.cpp:165-169: EXEC 0x03 command sets state.relativeNoteScaling = false
  • WavetablePlayer.cpp:283-300: Implemented fractional semitone scaling in FrqR processing when state.relativeNoteScaling is true
  • WavetablePlayer.cpp:493: Extended initialization loop from 5 to 6 register types including Filter/Vol wavetable
  • WavetablePlayer.h:100: Added WavetableState parameter to processFreqStep() method signature
  • WavetablePlayer.cpp:176: Updated processFreqStep() call to pass state reference

Mathematical Formula

  • Scaling Calculation: newFreq = currentFreq * (2^(1/12))^(frqR/32768)
  • Semitone Ratio: 2^(1/12) represents one semitone frequency ratio
  • Fractional Division: 1/32768 provides extremely fine-grained pitch control (±15-bit signed range)
  • Multiplicative Scaling: Preserves harmonic relationships across frequency range unlike additive offset

Architecture Benefits

  • Per-Wavetable Control: Each wavetable can independently enable/disable relative note scaling via EXEC commands
  • Backward Compatibility: Default disabled state preserves existing FrqR behavior as absolute frequency offset
  • Dynamic Mode Switching: EXEC commands allow real-time switching between scaling modes within same wavetable
  • State Initialization: Proper reset to false ensures predictable behavior on patch loading and note triggering

[v0.9.44] – 2025-10-05

Reference: UI Layout and Visual Consistency Improvements
Summary: Enhanced visual consistency across UI components with rounded borders, circuit background, and oscilloscope sizing improvements

Enhanced

  • Rounded Border Consistency: Updated WavetableSelectorComponent border to match patch manager styling with 2px thickness and proper corner rounding using bounds.reduced(1)
  • Circuit Component Background: Added dark green PCB-style background (0xff003300) to circuit area between wavetable selector and oscilloscope with matching rounded corners and border styling
  • Oscilloscope Height Alignment: Removed inner 5px margin from oscilloscope component making it same height as wavetable selector for consistent visual alignment
  • Seamless Component Integration: Eliminated gaps between wavetable selector, circuit component, and oscilloscope for unified visual appearance

Technical Implementation

  • WavetableSelectorComponent.cpp:331: Changed border from 4.0f to 2.0f thickness and added bounds.reduced(1) for proper rounded corner rendering
  • CircuitComponent.cpp:16-35: Implemented dark green background painting in area between wavetable selector and oscilloscope with automatic layout calculation
  • CircuitComponent.cpp:20-21: Background spans directly from wavetable selector right edge to oscilloscope left edge with no gaps
  • OscilloscopeComponent.cpp:21: Removed bounds.reduce(5.0f, 5.0f) inner margin for full-height display
  • OscilloscopeComponent.cpp:127: Updated border to use borderBounds.reduced(1) matching wavetable selector styling

Visual Improvements

  • Consistent Border Styling: All major UI components now use matching 2px border with 8px rounded corners and 1px inset
  • PCB Aesthetic Enhancement: Circuit background adds authentic hardware appearance bridging wavetable controls and audio visualization
  • Unified Component Heights: Wavetable selector, circuit area, and oscilloscope now align perfectly at same height eliminating visual discontinuity
  • Professional Polish: Cohesive visual design with consistent rounded borders and seamless component integration

Architecture Benefits

  • Dynamic Layout Calculation: Circuit background automatically adapts to component positions through area-based rendering
  • Consistent Styling Pattern: All components follow same border and corner radius conventions for unified visual language
  • Clean Component Boundaries: Proper use of reduced() ensures borders render cleanly within rounded corner areas

[v0.9.43] – 2025-10-03

Reference: Custom Icon Buttons for Patch Management
Summary: Enhanced Load and Save buttons with custom icons and improved visual styling

Added

  • LoadBankButton Class: Custom button component displaying Load1.png icon with “Load Bank” text label
  • SaveBankButton Class: Custom button component displaying Save1.png icon with “Save Bank” text label
  • Icon Integration: Added Load1.png and Save1.png to binary resources in CMakeLists.txt

Enhanced

  • Rounded Corners: Increased button corner radius from 4px to 8px for more polished appearance
  • Mouse-Over Highlighting: Added hover effect that brightens button background by 20% when mouse enters
  • Icon-Text Layout: Icons positioned on left (16px size, 8px padding) with text labels to the right
  • Consistent Styling: Both buttons use matching C64 colour scheme with BackgroundMedium base colour
  • Professional Polish: Custom painted buttons with proper state visualization (normal, hover, pressed)

Technical Implementation

  • PatchManagerComponent.h: Added LoadBankButton and SaveBankButton class definitions with custom paintButton() methods
  • Custom Paint Logic: Implemented icon rendering with proper scaling and text positioning using FontManager::getC64FontForEditors()
  • Colour Integration: Used C64Colors constants (Button::BackgroundMedium, Button::BackgroundDark, Button::Text) for consistent theming
  • Binary Resources: Load1.png (1212 bytes) and Save1.png (1248 bytes) embedded as BinaryData resources
  • Simplified Initialization: Removed redundant TextButton styling code, custom buttons handle all rendering internally

Architecture Benefits

  • Visual Consistency: Icon buttons match the professional appearance of other custom UI elements
  • Maintainable Design: Self-contained button classes with clear paintButton() implementations
  • Resource Efficiency: Embedded PNG icons loaded once and cached by JUCE ImageCache
  • Code Simplification: Custom buttons require minimal initialization compared to styled TextButtons

User Experience

  • Intuitive Interface: Icons provide immediate visual recognition of Load/Save functionality
  • Professional Appearance: Rounded corners and hover effects create polished, modern UI feel
  • Visual Feedback: Clear hover highlighting provides immediate feedback when interacting with buttons
  • Consistent Design: Buttons maintain C64 theme while adding modern visual polish

[v0.9.42] – 2025-10-02

Reference: Windows Compatibility Fix
Summary: Added pre-generated libsidplayfp headers for Windows builds eliminating autogen/configure dependency

Fixed

  • Windows Build Compatibility: Fixed missing siddefs-fpII.h header that prevented Windows builds without autotools
  • Cross-Platform Headers: Copied auto-generated headers to Source/libsidplayfp_compat/ directory for universal access
  • CMake Include Priority: Modified CMakeLists.txt to prioritize compatibility directory ensuring Windows finds pre-generated headers

Technical Implementation

  • Pre-Generated Headers: Copied siddefs-fpII.h from Linux build to Source/libsidplayfp_compat/ directory
  • CMakeLists.txt Enhancement: Added ${CMAKE_CURRENT_SOURCE_DIR}/Source/libsidplayfp_compat as first include directory for residfp target (line 119)
  • Include Path Priority: Compiler checks compatibility directory first, finding pre-generated headers before falling back to libsidplayfp source tree
  • No Fork Required: Avoided forking libsidplayfp by shipping generated headers in main project source tree

Architecture Benefits

  • Windows Native Support: Windows builds work out-of-the-box with MSVC/MinGW without requiring MSYS2/Cygwin autotools
  • Clean Submodule: Maintains upstream libsidplayfp as clean submodule without modifications or fork maintenance
  • Minimal Overhead: Small generated header files require minimal maintenance and rarely change between libsidplayfp versions
  • Universal Compatibility: Single codebase builds on Linux (with autotools) and Windows (without autotools) using same CMake configuration

User Experience

  • Simplified Windows Setup: Windows developers can build directly without installing MSYS2 or running autogen/configure
  • Consistent Build Process: Same CMake build process works identically on all platforms
  • Reduced Dependencies: Eliminates autotools dependency for Windows builds streamlining development workflow

[v0.9.41] – 2025-10-02

Reference: Build System Integration
Summary: Automated libsidplayfp submodule initialization and configuration for streamlined build process

Enhanced

  • Automated Submodule Setup: Updated build.sh to automatically configure libsidplayfp submodule before building
  • CMake Configuration: Added proper C++14 support and include paths for libsidplayfp headers
  • Build Script Integration: Integrated autoreconf and configure steps for libsidplayfp into build workflow

Technical Implementation

  • libsidplayfp Initialization: Added autoreconf -fi and ./configure steps to build.sh (Step 2)
  • Header File Generation: Automated generation of siddefs-fp.h via configure script before CMake runs
  • C++ Standard Compliance: Added HAVE_CXX14=1 compile definition and C++14 standard requirement for residfp library
  • Include Path Fix: Added libsidplayfp/src to include directories for sidcxx11.h header access
  • CMake Build Type: Updated build script to use RelWithDebInfo configuration matching VSCode CMake Tools settings

Fixed

  • Missing Header Files: Resolved “siddefs-fp.h: No such file or directory” compilation errors
  • C++11 Compiler Check: Fixed “This is not a C++11 compiler” errors by defining HAVE_CXX14 macro
  • Submodule Dependencies: Initialized exsid-driver submodule required by libsidplayfp configure script
  • Build Process Order: Ensured libsidplayfp configuration runs before CMake configuration step

Build Process

  • Step 1: Clean existing build directory
  • Step 2: Configure libsidplayfp (autoreconf + ./configure)
  • Step 3: Create build directory
  • Step 4: Run CMake with RelWithDebInfo
  • Step 5: Build with make -j$(nproc)

[v0.9.40] – 2025-10-02

Reference: libsidplayfp Edition
Summary: Major emulation upgrade replacing reSID with reSIDfp for improved filter accuracy, combined waveform modeling, and authentic SID chip behavior

Enhanced

  • reSIDfp Integration: Replaced original reSID library with libsidplayfp’s reSIDfp for significantly improved SID emulation accuracy
  • Floating-Point Filter Emulation: Advanced filter modeling using floating-point calculations instead of fixed-point for more accurate frequency response
  • Combined Waveforms: Enabled STRONG combined waveforms mode using parametrized model based on Kevtris samples for authentic pulse+sawtooth+triangle combinations
  • Improved Envelope Generator: Shift register-based envelope implementation matching real MOS 6581/8580 hardware behavior
  • Two-Pass Resampling: High-quality audio resampling with computational optimizations for better sound quality
  • 2024 Accuracy Updates: Benefits from active libsidplayfp development including oscillator leakage emulation and voice sync fixes

Fixed

  • Startup Click Elimination: Implemented 100ms linear fade-in preventing DC offset clicks from reSIDfp filter initialization
  • Patch Change Gate Triggers: Removed register backup/restore from chip model switching eliminating unwanted note triggers during patch loading
  • Audio Graininess: Removed 1.1x cycle multiplier causing resampler timing issues, ensuring clean audio output
  • Chip Model Switching: Simplified setChipModel() to only change chip model without reset or register manipulation

Technical Implementation

  • Git Submodule Migration: Replaced reSID submodule with libsidplayfp from https://github.com/libsidplayfp/libsidplayfp
  • CMakeLists.txt Refactoring: Updated build configuration for 17 reSIDfp source files including filters, integrators, waveform generators, and resamplers
  • SIDEngine.h/cpp Updates: Migrated from reSID namespace to reSIDfp namespace with API method name changes:
  • set_chip_model()setChipModel()
  • set_sampling_parameters()setSamplingParameters()
  • enable_filter()enableFilter()
  • SAMPLE_INTERPOLATERESAMPLE
  • chip_modelChipModel
  • cycle_countunsigned int
  • clock(delta_t, buf, n)clock(cycles, buf) with return value
  • Configuration Files: Created siddefs-fp.h and sidcxx11.h with proper C++20 configuration and compiler feature detection
  • Combined Waveforms Setup: Added sid->setCombinedWaveforms(STRONG) in SIDEngine constructor for maximum accuracy
  • Fade-In Architecture: Added fadeInSamplesRemaining counter with linear gain ramp (0.0 → 1.0) over 4410 samples preventing startup transients

Architecture Benefits

  • Active Development: libsidplayfp receives regular updates (12+ improvements in 2024) vs. older reSID codebase
  • Better Filter Accuracy: Floating-point calculations provide more precise filter modeling especially for 6581 chip characteristics
  • Authentic Combined Waveforms: Parametrized waveform model based on real hardware sampling provides accurate mixed waveform output
  • Improved Hardware Fidelity: Shift register envelope generator and oscillator leakage emulation match real chip behavior more closely
  • Professional Audio Quality: Two-pass resampling and optimised filters deliver cleaner output suitable for professional production

User Experience

  • Improved Sound Quality: More authentic SID chip emulation with better filter response and combined waveform accuracy
  • Click-Free Operation: Smooth fade-in on startup eliminates jarring DC offset clicks from filter initialization
  • Clean Patch Switching: No unwanted note triggers when changing patches or chip models during performance
  • Professional Reliability: Stable emulation based on actively maintained library with ongoing accuracy improvements

[v0.9.36] – 2025-10-01

Reference: Sandbox Compatibility & Security Hardening
Summary: Fixed file I/O sandbox violations and added comprehensive error handling for sandboxed DAW environments

Fixed

  • Sandbox Violations: Changed all file I/O from userDocumentsDirectory to userApplicationDataDirectory for sandbox compatibility
  • DAW Compatibility: Resolved file access issues in Logic Pro X, Ableton Live, Pro Tools AAX, and other sandboxed DAWs
  • File Operation Errors: Added comprehensive error handling with logging for failed directory creation and file operations

Changed

  • File Locations (All Platforms):
  • Windows: C:\Users\<username>\AppData\Roaming\reSIDue\ (was: Documents\reSIDue)
  • macOS: ~/Library/reSIDue/ (was: ~/Documents/reSIDue/)
  • Linux: ~/.config/reSIDue/ (was: ~/Documents/reSIDue/)

Enhanced

  • Error Logging: All file operations now log errors when directory creation or file I/O fails
  • Graceful Degradation: LogManager falls back to stderr output if log directory creation fails
  • User Feedback: File operations provide clear error messages through logging system

Technical Implementation

  • FileManager.cpp: Updated 5 file operations to use userApplicationDataDirectory with error handling
  • savePatchSetBinary(): Added directory creation validation and write error logging
  • loadPatchSetBinary(): Added file existence checks and read error logging
  • saveConfig(): Added directory creation error handling
  • loadConfig(): Added empty file warning
  • savePatchSetToFileV2(): Added JSON export error handling
  • PatchManagerComponent.cpp: Updated 5 file chooser locations to use userApplicationDataDirectory
  • Save/Load binary patch set file choosers
  • Import/Export JSON file choosers
  • getPatchSetFile(): Added directory creation error handling
  • LogManager.cpp: Updated log file location with stderr fallback
  • ensureInitialized(): Added directory creation validation with stderr fallback

Architecture Benefits

  • Sandbox Compliance: Plugin now works correctly in all major DAW sandbox environments
  • Security: Uses platform-standard application data directories following OS guidelines
  • Reliability: Comprehensive error handling prevents silent failures
  • User Experience: Clear error messages help diagnose file access issues
  • Cross-Platform: Proper platform-specific paths for Windows, macOS, and Linux

[v0.9.35] – 2025-09-30

Reference: Spring Cleaning Edition #3
Summary: Code cleanup removing unused methods and variables to improve codebase maintainability

Removed

  • Unused Variable: Removed loopIterations variable from PluginProcessor that was incremented but never read
  • Unused Methods: Removed getVoiceState() method from PluginProcessor (never called)
  • WavetableTableComponent Cleanup: Removed unused getRow(), setRow(), and const getTableData() methods

Enhanced

  • UI Text Consistency: Capitalized voice channel dropdown “off” to “Off” for consistent UI presentation

Technical Implementation

  • PluginProcessor.h:274: Removed unused loopIterations member variable declaration
  • PluginProcessor.cpp:137,237-245: Removed all loopIterations initialization, increment, and reset operations
  • PluginProcessor.h:93: Removed unused getVoiceState() method providing direct voice state access
  • WavetableTableComponent.h:405: Removed unused const getTableData() method (only mutable version used)
  • WavetableTableComponent.cpp:333-351: Removed unused getRow() and setRow() methods (19 lines of dead code)
  • WavetableSelectorComponent.cpp:44,57,589,606: Capitalized “off” to “Off” in voice channel dropdown

Architecture Benefits

  • Cleaner Codebase: Eliminated approximately 25 lines of dead code that served no purpose
  • Reduced Maintenance Burden: Fewer unused methods means less code to maintain during future development
  • Simplified API Surface: Removed redundant methods that duplicated existing functionality without adding value
  • Code Quality Improvement: Systematic cleanup improves overall codebase maintainability

[v0.9.34] – 2025-09-30

Reference: JUCE framework upgrade to version 7.0.6 and build system restoration
Summary: Updated JUCE framework to version 7.0.6 and resolved build errors by restoring missing modules and OpenGL functionality

Fixed

  • JUCE Submodule Corruption: Resolved corrupted JUCE submodule by performing clean checkout to JUCE 7.0.6 tag with proper file structure
  • Missing Module Dependencies: Restored missing juce_opengl and juce_gui_extra modules that were causing build failures in CMake configuration
  • OpenGL Functionality: Re-enabled OpenGL hardware acceleration support in main CMakeLists.txt after module restoration
  • Build System Integrity: Fixed broken module references in JUCE modules CMakeLists.txt ensuring only available modules are included

Technical Implementation

  • Framework Upgrade: Updated from JUCE 8.0.10 to stable JUCE 7.0.6 for better compatibility and feature support
  • Module Availability: Verified all required JUCE modules exist and are properly configured for audio plugin development
  • CMake Configuration: Cleaned up module references removing non-existent modules while preserving essential functionality
  • Build Verification: Confirmed successful compilation of both Standalone and VST3 plugin formats

Architecture Benefits

  • Stable Foundation: JUCE 7.0.6 provides stable framework foundation for continued development and deployment
  • OpenGL Acceleration: Hardware acceleration capabilities fully restored for improved GUI performance
  • Build Reliability: Eliminated CMake configuration errors ensuring consistent build success across development environments
  • Module Integrity: All JUCE modules properly aligned with framework version preventing future build conflicts

[v0.9.33] – 2025-09-29

Reference: JUCE Path-based circuit rendering system and code cleanup
Summary: Converted CircuitComponent to use JUCE’s Path class for optimised line rendering and removed unused methods for cleaner codebase

Enhanced

  • JUCE Path Integration: Replaced manual line-by-line drawing with JUCE’s Path class using path.lineTo() for optimised rendering performance
  • Cached Path System: Added cached juce::Path to CircuitPath struct with automatic regeneration when waypoints change for improved rendering efficiency
  • Path Stroke Optimisation: Single g.strokePath() call replaces multiple individual line draws reducing graphics API overhead
  • Connection Dot Rendering: Enhanced connection dots with square boxes instead of circular ellipses for more authentic PCB aesthetic

Technical Implementation

  • CircuitComponent.h: Added cachedPath and pathNeedsUpdate members to CircuitPath struct with constructor initialization
  • CircuitComponent.cpp: Implemented generatePathForCircuit() method using path.startNewSubPath() and path.lineTo() for efficient path creation
  • Rendering Architecture: Modified drawCircuitPath() to use PathStrokeType with single stroke operation instead of loop-based line drawing
  • Path Caching: Automatic path regeneration only when waypoints change via pathNeedsUpdate flag preventing unnecessary recalculation

Code Quality

  • Dead Code Removal: Eliminated unused updateCircuitColors() method that was never called from anywhere in codebase
  • API Cleanup: Removed unused drawCircuitPath(size_t pathIndex) overload method that had no callers
  • Method Consolidation: Streamlined CircuitComponent API by removing redundant methods improving maintainability

Performance Benefits

  • Single Stroke Operations: JUCE Path rendering uses optimised graphics pipeline instead of multiple individual line draw calls
  • Reduced Graphics Overhead: Path-based rendering leverages GPU acceleration when available for smoother visual performance
  • Smart Caching: Paths only regenerated when geometry changes eliminating redundant path calculations during paint cycles
  • Future Flexibility: Path-based architecture enables easy addition of curves, rounded corners, and complex shapes

Architecture Benefits

  • JUCE Framework Alignment: Uses JUCE’s native path rendering system following framework best practices for graphics operations
  • Memory Efficient Caching: Cached paths stored per-circuit reducing computation while maintaining reasonable memory usage
  • Visual Consistency: JUCE’s path stroking provides consistent line rendering across different platforms and graphics backends
  • Maintainable Code: Cleaner codebase with fewer unused methods and streamlined rendering architecture

[v0.9.32] – 2025-09-29

Reference: Circuit line animation system with 50Hz timer integration
Summary: Added dynamic circuit line colour animation using 50Hz timer for realistic PCB trace flickering effects

Added

  • Random Line Animation: Implemented animateRandomLine() method in CircuitComponent for dynamic colour variations at 50Hz intervals
  • Timer Integration: Connected circuit animation to existing GuiUpdateTimer system eliminating need for separate animation timer
  • Callback Architecture: Added triggerCircuitAnimation() and setCircuitAnimationCallback() methods to PluginProcessor following established pattern
  • Performance Optimisation: Separated geometry initialization from colour updates preventing unnecessary path rebuilding during animation

Enhanced

  • ±40% Green Variation: Circuit lines randomly vary green component by ±40% from base colour creating realistic PCB trace flickering
  • Base Colour Preservation: Added baseColor member to CircuitPath struct ensuring variations always calculated from original colour preventing drift
  • Individual Line Updates: Optimised repainting to only refresh affected path area instead of entire component during colour changes
  • Full Opacity Animation: All animated lines maintain full opacity (alpha = 255) for maximum visual impact

Technical Implementation

  • CircuitComponent.h: Added animateRandomLine() method and baseColor member to CircuitPath struct for consistent variation calculations
  • CircuitComponent.cpp: Implemented random path selection with ±40% green variation using juce::Random::getSystemRandom() and proper colour clamping
  • PluginProcessor.h/cpp: Added circuit animation callback system with triggerCircuitAnimation() method and circuitAnimationCallback member
  • PluginEditor.cpp: Connected circuit animation to GuiUpdateTimer with MessageManager::callAsync() for thread-safe GUI updates
  • Performance Architecture: Geometry rebuilding only occurs when component areas change via geometryInitialized flag preventing unnecessary recalculation

Architecture Benefits

  • Stable Geometry: Circuit paths maintain fixed waypoints while allowing dynamic colour changes for optimal performance
  • Thread-Safe Animation: All GUI updates properly marshaled to message thread preventing JUCE assertion failures
  • Efficient Rendering: Individual path repainting with calculated bounds minimizes drawing overhead during animation
  • Consistent Pattern: Follows established callback architecture used by oscilloscope and wavetable updates

User Experience

  • Live Circuit Effect: PCB traces now flicker with realistic green variations simulating active circuit board appearance
  • Authentic Hardware Feel: Dynamic animation enhances the authentic C64 hardware emulation experience
  • Non-Intrusive Animation: Circuit animation provides visual enhancement without interfering with existing UI functionality
  • Performance Optimised: Animation runs smoothly at 50Hz without impacting audio processing or other UI responsiveness

[v0.9.31] – 2025-09-29

Reference: PCB-style circuit visualization component implementation
Summary: Added visual circuit board component drawing PCB-style traces connecting SID chip to UI components for enhanced hardware authenticity

Added

  • CircuitComponent Class: New visual component implementing PCB-style circuit traces between SID chip and interface elements
  • SID Chip Pin Mapping: Authentic MOS 6581 pin layout with address pins (A0-A4), data pins (D0-D7), and audio output pin positioning
  • Dynamic Circuit Routing: Automatic circuit path generation based on component positions with complex waypoint routing to avoid visual overlap
  • PCB Aesthetic Elements: Green circuit traces with connection dots at waypoints for authentic circuit board appearance

Enhanced

  • Component Integration: Circuit paths dynamically update when chip area, wavetable selector area, or oscilloscope area positions change
  • Visual Data Flow: Circuit traces visually represent how data flows from SID chip to wavetable controls and audio output to oscilloscope
  • Hardware Authenticity: Visual representation matches real circuit board connections enhancing the authentic C64 hardware emulation experience
  • Professional Visual Polish: Circuit board aesthetic adds professional hardware appearance to the plugin interface

Technical Implementation

  • CircuitComponent.cpp: Complete implementation with pin positioning based on MOS 6581 layout using relative coordinates within chip area
  • Address/Data Pin Connections: A0-A4 and D0-D7 pins connect to wavetable selector with sophisticated multi-waypoint routing using 3-pixel trace spacing
  • Audio Pin Connection: Audio output pin connects to oscilloscope input with simplified waypoint routing for clean visual appearance
  • Dynamic Path Updates: updateCircuitPaths() method automatically recalculates circuit routing when component areas change via setChipArea(), setWavetableSelectorArea(), and setOscilloscopeArea()
  • Rendering System: drawCircuitPath() method renders circuit segments with specified line width and connection dots for authentic PCB appearance

Architecture Benefits

  • Component Isolation: CircuitComponent operates independently with mouse events passing through via setInterceptsMouseClicks(false, false)
  • Scalable Design: Circuit paths automatically adapt to different component sizes and positions for flexible UI layout
  • Visual Consistency: Green trace colour (#004000) and standardized connection dots provide consistent circuit board aesthetic
  • Performance Optimised: Efficient path rendering with pre-calculated waypoints minimizing drawing overhead

User Experience

  • Enhanced Visual Appeal: Circuit board traces add professional hardware appearance making the plugin more visually engaging
  • Educational Value: Visual representation helps users understand how SID chip connects to various interface controls
  • Authentic Hardware Feel: PCB-style visualization reinforces the authentic C64 hardware emulation experience
  • Non-Intrusive Design: Circuit traces provide visual enhancement without interfering with existing UI functionality

[v0.9.30] – 2025-09-28

Reference: ReTrig functionality implementation and wavetable activation optimisation
Summary: Implemented ReTrig behavior in wavetable start() method and modified activation logic to always enable all wavetables on note-on events

Enhanced

  • ReTrig Implementation: Added ReTrig functionality to WavetablePlayer::start() method – wavetables with ReTrig enabled (flag[1]) now reset their state to position 0 on note-on events
  • Always-Active Wavetables: Modified wavetable activation logic to always activate all wavetables on start() regardless of Loop setting, ensuring immediate responsiveness
  • State Reset Control: ReTrig flag now properly controls whether wavetable state (currentRow, execCount, etc.) resets on new note events

Technical Implementation

  • WavetablePlayer.cpp:80-83: Added ReTrig check using allWavetablesControl[i][currentTable][1] to conditionally reset wavetable state
  • WavetablePlayer.cpp:84: Changed activation logic to always set states[i].isActive = true instead of conditional activation based on Loop settings
  • WavetablePlayer.cpp:86-95: Preserved original Loop-based activation logic in comments for reference while implementing new always-active approach
  • Flag Integration: ReTrig functionality leverages existing control flag system (flag[1] = ReTrig) for consistent wavetable behavior control

User Experience Benefits

  • Responsive Wavetables: All wavetables now immediately respond to note-on events providing instant feedback regardless of Loop configuration
  • Controlled Reset Behavior: Users can enable/disable ReTrig per wavetable to control whether note-on events reset wavetable position or continue from current position
  • Predictable Operation: Simplified activation logic eliminates confusion about when wavetables will respond to MIDI input

Architecture Benefits

  • Consistent Activation: Uniform wavetable activation ensures all wavetables are ready for processing when notes are triggered
  • Flexible Reset Control: ReTrig flag provides granular control over wavetable reset behavior without affecting activation status
  • Simplified Logic: Removed complex conditional activation reducing potential edge cases and improving reliability

[v0.9.29] – 2025-09-27

Reference: CH10 Voice Channel Re-evaluation Fix
Summary: Fixed MIDI channel 10 patch changes not applying correct sound by adding voice channel re-evaluation after patch loading

Fixed

  • CH10 Sound Application: Fixed issue where MIDI channel 10 patch changes would update patch index but not apply correct sound to playing voices
  • Voice Channel Re-evaluation: Added voice MIDI channel compatibility check after patch loading to ensure voice processes with correct settings
  • Patch Change Effectiveness: MIDI channel 10 notes now properly trigger both patch change and correct audio output matching the new patch

Technical Implementation

  • PluginProcessor.cpp:345: Enhanced CH10 patch change logging with note number and target patch index for debugging
  • PluginProcessor.cpp:48-53: Added voice channel re-evaluation logic after setCurrentProgram() call in MIDI channel 10 handling
  • Channel Compatibility Check: After patch change, voice MIDI channel settings are re-checked against incoming MIDI channel to ensure voice remains eligible for processing
  • Skip Logic Enhancement: Voice processing skips if new patch assigns incompatible MIDI channel to the voice, preventing incorrect sound output

Root Cause Resolution

  • Mid-Loop Patch Changes: Patch loading via setCurrentProgram() was changing voice MIDI channel settings while voice processing loop was active
  • Voice Assignment Mismatch: Original voice selection criteria became invalid after patch changed voice channel assignments, causing voice to process with wrong channel mapping
  • Architectural Timing: Fixed timing issue where patch change affected global settings but voice processing continued with pre-change voice selection

User Experience Benefits

  • Accurate CH10 Response: MIDI channel 10 percussion mapping now produces correct sounds matching the intended patch for each MIDI note
  • Immediate Audio Feedback: Patch changes via MIDI channel 10 notes now immediately affect audio output as expected
  • Reliable Percussion Workflow: Drum-style MIDI channel 10 operation now works consistently with proper patch-to-sound mapping

[v0.9.28] – 2025-09-26

Reference: MIDI Channel 10 Percussion Patch Mapping
Summary: Added special percussion mapping for MIDI channel 10 enabling automatic patch switching based on MIDI note numbers

Added

  • MIDI Channel 10 Handler: Special processing for MIDI channel 10 (standard percussion channel) in handleNoteOn method
  • Note-to-Patch Mapping Array: MIDI notes on channel 10 trigger specific patch changes via midiNoteToPatchMap array lookup
  • Automatic Patch Switching: When mapped MIDI note received on channel 10, plugin automatically calls setCurrentProgram() for corresponding patch
  • Drum Kit Style Operation: Enables drum-kit workflow where different MIDI notes (kick, snare, hi-hat) trigger different instrument patches
  • Dynamic Performance Control: Real-time patch changes during MIDI performance for percussion-style patch management

Technical Implementation

  • PluginProcessor.cpp:339-347: Added MIDI channel 10 check with midiNoteToPatchMap array lookup in handleNoteOn method
  • Patch Triggering Logic: If midiNoteToPatchMap[midiNote] != -1, calls setCurrentProgram(patchIndex) for automatic patch switching
  • Channel-Specific Processing: Only processes note-to-patch mapping on channel 10, preserving normal operation for other channels
  • Integration with Existing System: Works alongside existing MIDI channel filtering and voice processing logic

User Experience Benefits

  • Percussion Workflow: MIDI channel 10 can now trigger different patches automatically based on note number (C-1 → Patch 5, D-1 → Patch 12, etc.)
  • Live Performance Enhancement: Enables dynamic patch switching during performance using standard drum channel conventions
  • Hardware Controller Integration: Works with drum pads and percussion controllers that send on MIDI channel 10
  • Professional Workflow: Follows established MIDI percussion mapping conventions for intuitive operation

Architecture Benefits

  • Backward Compatibility: Existing MIDI processing preserved – only adds new functionality for channel 10
  • Existing Data Integration: Uses pre-existing midiNoteToPatchMap array that was added in v0.9.20
  • Minimal Code Impact: Simple addition to existing handleNoteOn method without affecting other MIDI processing
  • Standard MIDI Compliance: Follows MIDI specification where channel 10 is reserved for percussion instruments

[v0.9.27] – 2025-09-26

Reference: MIDI Channel Filtering Implementation
Summary: Enhanced handleNoteOn and handleNoteOff methods to respect per-voice MIDI channel settings for proper voice filtering

Added

  • MIDI Channel Parameter: Added midiChannel parameter to handleNoteOn() and handleNoteOff() method signatures
  • Voice Channel Filtering: Implemented MIDI channel checking logic in both note on/off handlers
  • Channel Validation Logic: Added comprehensive channel filtering (-1=off, 0=Any, 1-16=specific channel)
  • Message Processing Enhancement: Updated MIDI message processing to pass msg.getChannel() to note handlers

Enhanced

  • Per-Voice Channel Control: Each voice now only responds to MIDI notes on its configured channel
  • Channel Setting Integration: Leverages existing getVoiceMidiChannel() method for channel configuration retrieval
  • Skip Logic Implementation: Voices skip processing when channel doesn’t match incoming MIDI channel
  • Consistent Behavior: Both note-on and note-off events respect identical channel filtering logic

Technical Details

  • PluginProcessor.h:208-209: Updated method signatures to include int midiChannel parameter
  • PluginProcessor.cpp:180,184: Updated MIDI message processing to pass msg.getChannel() to handlers
  • PluginProcessor.cpp:329-337: Added channel filtering logic in handleNoteOn() before voice processing
  • PluginProcessor.cpp:396-404: Added identical channel filtering logic in handleNoteOff() for consistency
  • Channel Logic: -1 (off) = skip voice, 0 (Any) = process all channels, 1-16 = process specific channel only

Architecture Benefits

  • Proper Voice Isolation: Voices configured for specific channels no longer respond to other channels
  • Existing UI Integration: Works seamlessly with current channel selection dropdowns in WavetableSelectorComponent
  • Patch Data Compatibility: Uses existing voiceMidiChannel field from PatchData structure
  • Backward Compatibility: Maintains existing API while adding channel awareness to MIDI processing

[v0.9.26] – 2025-09-26

Reference: Binary Format MIDI Note Mapping Completion
Summary: Added complete MIDI note to patch mapping serialization to binary .reSIDue format for consistency with JSON format

Added

  • Binary MIDI Note Mapping: Added complete 128-entry MIDI note to patch mapping array serialization to createBinaryV3() method
  • Binary MIDI Note Loading: Added corresponding deserialization in parseBinaryV3() method to restore MIDI note mappings from binary files
  • Format Consistency: Binary .reSIDue format now includes same MIDI note mapping data as JSON format ensuring feature parity
  • Embedded Patch Data: Added patches.reSIDue file as embedded binary resource in CMakeLists.txt for future default patch system integration

Technical Details

  • FileManager.cpp:283-288: Added MIDI note to patch mapping write operation as final step in createBinaryV3()
  • FileManager.cpp:636-652: Added MIDI note to patch mapping read operation in parseBinaryV3() with proper bounds checking
  • Complete Array Serialization: All 128 MIDI note slots written/read as integers using existing appendData/readData helpers
  • Error Handling: Added safe reading with early exit if data is incomplete during MIDI mapping restoration
  • CMakeLists.txt:74: Added patches/patches.reSIDue to juce_add_binary_data for embedded resource access

Architecture Benefits

  • Format Equivalence: Binary and JSON formats now preserve identical data sets eliminating format-specific feature limitations
  • Data Completeness: MIDI note to patch mappings properly preserved in binary saves ensuring no data loss during file operations
  • Consistent Implementation: Uses same patterns as existing binary serialization with proper error handling and bounds checking

[v0.9.25] – 2025-09-25

Reference: MIDI Note to Patch Mapping Interface Implementation
Summary: Added CH10 dropdown for MIDI note to patch mapping with complete UI integration

Added

  • CH10 MIDI->Patch Dropdown: Added dropdown in WavetableSelectorComponent for assigning MIDI notes to patches
  • Voice Label Optimisation: Changed voice labels from “Voice 1/2/3” to compact “V1:/V2:/V3:” format
  • Layout Enhancement: Repositioned voice labels to the left of channel selector dropdowns for horizontal layout
  • 98 MIDI Note Support: Dropdown populated with full MIDI note range from “KT” (-1) to “C8” (108) using existing midiNoteArray
  • Patch Data Integration: Connected to existing PatchData.assignedMidiNote field with real-time synchronization

Changed

  • WavetableSelectorComponent Layout: Expanded from 6 to 7 columns to accommodate CH10 dropdown
  • Voice Control Area Width: Increased voice control section from 70px to 90px for better spacing
  • Dropdown Dimensions: Unified all dropdown heights to match CH10 dropdown dimensions
  • Label Positioning: Moved voice labels from vertical (above) to horizontal (left) layout
  • Font Sizing: Applied smaller font (7.0f) to CH10 column header for better fit

Technical Details

  • WavetableSelectorComponent.h: Added midiToPatchComboBox, midiToPatchLabel, and callback support
  • WavetableSelectorComponent.cpp:273-290: Dropdown initialization with midiNoteArray population
  • WavetableSelectorComponent.cpp:640-660: ComboBox change handler for MIDI note selection
  • WavetableSelectorComponent.cpp:689-730: setAssignedMidiNote() method with validation and fallback
  • WavetableSelectorComponent.cpp:535-550: refreshFromPatchData() integration for patch switching
  • PatchManagerComponent.cpp:507-509: Added refreshFromPatchData() call to refreshUIAfterPatchLoad()
  • Layout Architecture: 7-column header with V1:/V2:/V3: labels positioned left of dropdowns
  • Data Flow: Direct patch data updates ensure synchronization across all patch management operations

[v0.9.24] – 2025-09-25

Reference: Spring Cleaning Edition #2
Summary: Code cleanup – removed unused methods from PatchManagerComponent

Removed

  • Dead Code Elimination: Removed unused methods from PatchManagerComponent to clean up codebase
  • PatchManagerComponent.h:50: Removed unused getLoadedFileName() declaration that was never implemented
  • PatchManagerComponent.cpp:512-539: Removed unused PatchData::toJSON() method (28 lines of dead code)
  • PatchManagerComponent.cpp:541-602: Removed unused PatchData::fromJSON() method (62 lines of dead code)

Technical Details

  • Code Analysis: Systematic analysis identified 3 unused methods totaling 90+ lines of dead code
  • JSON Serialization Cleanup: Removed obsolete JSON serialization methods since project migrated to binary .reSIDue format
  • Header Cleanup: Removed method declaration that was never implemented
  • Codebase Optimisation: Improved maintainability by eliminating unused functionality

[v0.9.23] – 2025-09-25

Reference: Filter/Volume Patch Index Persistence Fix
Summary: Fixed Filter/Volume wavetable index not persisting when switching between patches

Fixed

  • Filter/Volume Index Persistence: Fixed Filter/Volume wavetable index changes being lost when switching patches by saving changes directly to patch data
  • WavetableSelectorComponent Data Consistency: Filter/Volume index changes now saved to patches[currentPatchIndex].globalFilterVolIndex like voice-specific wavetable indexes
  • Patch Data Architecture Alignment: Eliminated inconsistency where voice-specific indexes were saved to patch data but Filter/Volume index was only stored locally

Technical Details

  • WavetableSelectorComponent.cpp:173-182: Added patch data update in filterVolSelector.onTextChange callback
  • WavetableSelectorComponent.cpp:445-454: Added patch data update in setFilterVolTableNumber() method
  • Root Cause: Filter/Volume changes were only stored in local filterVolTableNumber variable, causing refreshFromPatchData() to restore original values when switching patches

[v0.9.22] – 2025-09-24

Reference: JUCE Thread Safety Fix
Summary: Fixed JUCE Component.cpp:1609 assertion failures during MIDI note playback

Fixed

  • GUI Thread Safety: Fixed JUCE Component.cpp:1609 assertion failures occurring during MIDI note playback by wrapping GUI callback operations with MessageManager::callAsync()
  • Wavetable Row Updates: Fixed updateCurrentPlaybackRow() calls being executed from non-message thread
  • Oscilloscope Updates: Fixed updateDisplay() calls triggering repaint() from timer thread
  • MIDI Indicator Updates: Fixed triggerMidiActivity(), setGateOn(), and setGateOff() methods calling repaint() directly from MIDI processing thread

Technical Details

  • PluginEditor.cpp:100-107: Wrapped wavetable row update callback with MessageManager::callAsync()
  • PluginEditor.cpp:118-125: Wrapped oscilloscope update callback with MessageManager::callAsync()
  • PluginEditor.cpp:45-52: Wrapped MIDI indicator activity callback with MessageManager::callAsync()
  • PluginEditor.cpp:79-86: Wrapped MIDI gate on callback with MessageManager::callAsync()
  • PluginEditor.cpp:88-95: Wrapped MIDI gate off callback with MessageManager::callAsync()

Root Cause

  • GUI components were being updated directly from non-message threads (timer threads and MIDI processing callbacks)
  • JUCE requires all GUI operations (including repaint() calls) to occur on the message thread
  • The assertions were triggered by Component::repaint() calls from wrong thread context

[v0.9.21] – 2025-09-24

Reference: Spring Cleaning Edition #1
Summary: Code cleanup and maintenance improvements

Changed

  • Version update to v0.9.21 “Spring Cleaning Edition #1”

[v0.9.20] – 2025-09-23

Reference: MIDI Note to Patch Mapping System Implementation
Summary: Added complete MIDI note to patch mapping system with JSON serialization for persistent note-to-patch assignments

Added

  • MIDI Note to Patch Mapping: New std::array<int, 128> midiNoteToPatchMap in PluginProcessor for mapping MIDI notes (0-127) to patch indexes
  • Array Initialization: All mapping slots initialized to -1 (no mapping) in constructor for consistent default state
  • Public Accessor Methods: Added getMidiNoteToPatchMap() const and non-const methods for external access to mapping array
  • JSON Serialization: Complete array serialization in savePatchSetToFileV2() preserving all 128 mapping slots
  • JSON Deserialization: Loading support in loadAllPatchesFromPatchSet() with bounds checking and safe array restoration
  • File Format Extension: New "midiNoteToPatchMap" property at JSON root level containing complete mapping array

Technical Implementation

  • PluginProcessor.h:190: Added std::array<int, 128> midiNoteToPatchMap member variable with -1 sentinel values
  • PluginProcessor.h:158-160: Added public getter methods following existing getPatches() pattern
  • PluginProcessor.cpp:73: Constructor initialization using midiNoteToPatchMap.fill(-1) for default state
  • FileManager.cpp:1317-1324: Save implementation dumping complete 128-element array to JSON
  • FileManager.cpp:1666-1674: Load implementation with array bounds checking and safe restoration

Data Format

  • Array Structure: Direct index mapping where midiNoteToPatchMap[midiNote] = patchIndex
  • Sentinel Value: -1 indicates no mapping assigned for that MIDI note
  • Complete Persistence: All 128 slots saved/loaded regardless of assignment status
  • JSON Format: "midiNoteToPatchMap": [-1, -1, 5, -1, 12, ...] with array index as MIDI note number

[v0.9.19] – 2025-09-23

Reference: Single SID Instance Architecture – Critical Memory Safety and Authenticity Fix
Summary: Major architectural transformation from three separate SID instances to single shared SID instance, eliminating Valgrind memory errors and providing authentic C64 hardware emulation

Fixed

  • Critical Memory Safety: Eliminated uninitialized filter state conflicts causing Valgrind “Use of uninitialised value” errors in reSID filter processing
  • Architecture Flaw: Replaced incorrect three-SID design with authentic single-SID architecture matching real C64 hardware
  • Filter State Issues: Fixed filter register conflicts between multiple SID instances that caused unstable audio behavior
  • Performance Overhead: Eliminated 3× redundant filter processing and multiple render calls with audio mixing

Added

  • SIDEngine Class: New core engine wrapping single reSID instance with shared filter state for all voices
  • Voice Register Mapping: Automatic translation of voice-relative addresses to absolute SID register addresses
  • Voice 1: 0x00-0x06 (FREQ_LO, FREQ_HI, PW_LO, PW_HI, CTRL, ATTACK_DECAY, SUSTAIN_RELEASE)
  • Voice 2: 0x07-0x0D (same register types, different addresses)
  • Voice 3: 0x0E-0x14 (same register types, different addresses)
  • Shared: 0x15-0x18 (Filter Cutoff Low/High, Resonance/Filter Routing, Mode/Volume)
  • SIDVoice Wrapper System: Backwards-compatible interface maintaining existing API while delegating to shared engine
  • Address Translation: mapRegisterAddress() method handles voice-relative to absolute address mapping automatically

Changed

  • Rendering Architecture: Single unified render call outputs all voices mixed together instead of separate voice rendering
  • Filter Behavior: Filter/volume registers now correctly affect all voices simultaneously (authentic C64 behavior)
  • Memory Management: Single SID instance eliminates memory duplication and state conflicts
  • PluginProcessor Structure:
  • Added SIDEngine sidEngine (single shared instance)
  • Added SIDVoice sidVoice0/1/2 (wrapper instances pointing to shared engine)
  • Added getSIDVoice(int index) helper method for backwards compatibility

Technical Implementation

  • SIDEngine.h/cpp: Complete rewrite implementing single SID instance with proper voice delegation
  • PluginProcessor.h:610-613: Updated member variables to use single SID engine with voice wrappers
  • PluginProcessor.cpp:14-16: Constructor initialization using dependency injection pattern
  • PluginProcessor.cpp:280-289: Added getSIDVoice() helper method for index-based voice access
  • PluginProcessor.cpp:100-101: Unified SID reset and sample rate setting on single engine
  • PluginProcessor.cpp:485: Optimised renderAllActiveVoices() to use single SID render call
  • Constructor Pattern: SIDVoice(SIDEngine* engine, int voiceIndex) linking wrappers to shared engine
  • Address Mapping: Voice registers automatically mapped to correct SID chip addresses
  • Delegation Pattern: All SIDVoice operations delegate to shared SIDEngine with proper voice context

Architecture Benefits

  • Authentic Emulation: Single SID chip with three voices exactly matching real C64 hardware architecture
  • Memory Safety: Eliminates all uninitialized filter state errors detected by Valgrind static analysis
  • Performance: Single render call instead of three separate calls with significantly reduced CPU overhead
  • Correctness: Shared filter registers properly affect all voices as in authentic C64 behavior
  • Maintainability: Simplified architecture with clear separation between shared engine and voice interfaces
  • API Compatibility: Existing wavetable and MIDI code continues to work without modification

[v0.9.18] – 2025-09-22

Reference: Patch MIDI note assignment support
Summary: Added assignedMidiNote field to PatchData structure for MIDI note-to-patch mapping functionality

Added

  • MIDI Note Assignment: Added assignedMidiNote field to PatchData struct for mapping specific MIDI notes to patches (-1 = None, 0-127 = specific note)
  • File Format Support: Integrated assignedMidiNote into both JSON and binary patch file formats ensuring complete persistence
  • Backward Compatibility: New field defaults to -1 (None) maintaining compatibility with existing patch sets

Technical Implementation

  • PluginProcessor.h:18: Added int assignedMidiNote = -1 field to PatchData struct with proper initialization
  • FileManager.cpp:1229: Added assignedMidiNote to JSON patch serialization in savePatchSetToFileV2()
  • FileManager.cpp:1618: Added assignedMidiNote to JSON patch deserialization in loadAllPatchesFromPatchSet()
  • FileManager.cpp:143: Added assignedMidiNote to binary format in createBinaryV3()
  • FileManager.cpp:358: Added assignedMidiNote to binary parsing in parseBinaryV3()

Data Structure

  • Field Type: Integer field supporting values -1 (None/off), 0-127 (specific MIDI note assignment)
  • Default Value: -1 (None) ensuring backward compatibility with existing patches
  • File Persistence: Fully supported in both JSON v3.0 and binary v3.0 patch formats

[v0.9.17] – 2025-09-18

Reference: JUCE architecture compliance – GUI timer separation fix
Summary: Fixed JUCE separation of concerns violation by moving GuiUpdateTimer from PluginProcessor to PluginEditor

Fixed

  • JUCE Architecture Violation: Moved GuiUpdateTimer from PluginProcessor to PluginEditor following proper JUCE separation of concerns principles
  • GUI Timer Lifecycle: Timer now properly starts/stops with editor window instead of running continuously in audio processor
  • Private Member Access: Added public trigger methods triggerWavetableRowUpdate() and triggerOscilloscopeUpdate() for clean GUI-to-processor communication
  • Resource Management: GUI timer now stops when editor closes in VST environments, preventing unnecessary background processing

Technical Implementation

  • PluginEditor.h:44: Added std::unique_ptr<GuiUpdateTimer> guiUpdateTimer member to editor class
  • PluginEditor.cpp:7-38: Moved GuiUpdateTimer class definition from PluginProcessor.cpp to PluginEditor.cpp
  • PluginEditor.cpp:447-448: Initialize and start timer in editor constructor (50Hz = 20ms intervals)
  • PluginEditor.cpp:231-235: Stop and reset timer in editor destructor before component destruction
  • PluginProcessor.h:147-148: Added public trigger methods for GUI timer communication
  • PluginProcessor.cpp:773-787: Implemented trigger methods that safely call private callbacks
  • Removed: Timer member, initialization, cleanup, and friend class declaration from PluginProcessor

Architecture Improvements

  • Proper Separation of Concerns: Audio processor handles audio, editor handles GUI updates as per JUCE best practices
  • Correct Lifecycle Management: Timer only runs when GUI is open, eliminating unnecessary processing when editor is closed
  • Clean API Design: Public trigger methods provide controlled access to processor callbacks without exposing private members
  • Thread Safety: Maintained existing thread safety while improving architectural compliance

[v0.9.16] – 2025-09-18

Reference: VST multi-instance safety and static variable fixes
Summary: Fixed critical static variable issues preventing safe multi-instance VST operation and eliminated compilation errors

Fixed

  • Static Variable Multi-Instance Issues: Moved static audio processing variables from global scope to PluginProcessor instance members preventing state sharing between plugin instances
  • Cache Initialization Threading: Fixed static cache initialization flags in wavetable components to be per-instance preventing race conditions when multiple plugin instances load simultaneously
  • Logging Thread Safety: Created thread-safe LogManager singleton with proper std::once_flag and std::mutex protection replacing problematic static logging variables
  • Compilation Error: Fixed LogManager.h compilation failure by replacing non-existent <JuceHeader.h> with proper <juce_data_structures/juce_data_structures.h> include

Technical Implementation

  • PluginProcessor.h:250-254: Added instance member variables filterVolSamplesUntilNext50Hz, debugCounter, and loopIterations to replace static variables
  • LogManager.h/cpp: Implemented thread-safe singleton pattern with std::once_flag initFlag and std::mutex logMutex for safe multi-instance logging
  • FreqWavetableComponent.h:99, SingleValueWavetableComponent.h:95, PulsWavetableComponent.h:93: Added per-instance mutable bool cacheInitialized = false flags
  • JUCE Include Fix: Replaced incorrect <JuceHeader.h> with project-standard <juce_data_structures/juce_data_structures.h> include

Multi-Instance Safety

  • Audio Processing Isolation: Each plugin instance now maintains separate audio processing state preventing cross-instance interference
  • Thread-Safe Logging: LogManager singleton ensures safe concurrent logging from multiple plugin instances without data corruption
  • Cache Independence: Wavetable cache initialization is now per-component eliminating shared state between plugin instances
  • Compilation Stability: All files now compile correctly with proper JUCE module includes matching project architecture

[v0.9.15] – 2025-09-16

Reference: VST UI close crash fix
Summary: Fixed critical crash when closing VST plugin UI by resolving race condition between GUI timer and editor destruction

Fixed

  • VST UI Close Crash: Fixed critical bug where closing VST plugin UI crashed host due to GUI timer continuing to execute callbacks after UI components were destroyed
  • Race Condition Resolution: Added proper timer cleanup in PluginProcessor destructor to stop GuiUpdateTimer before destruction preventing callback execution on destroyed UI
  • Missing Callback Cleanup: Added missing setOscilloscopeUpdateCallback(nullptr) to editor destructor ensuring all GUI callbacks are properly cleared
  • Timer Lifecycle Management: Enhanced destructor sequence to properly stop and reset GUI update timer preventing post-destruction callback access

Technical Implementation

  • PluginProcessor.cpp:106-114: Enhanced processor destructor with proper guiUpdateTimer cleanup including stopTimer() and reset() calls
  • PluginEditor.cpp:216: Added missing processorRef.setOscilloscopeUpdateCallback(nullptr) to complete callback cleanup in editor destructor
  • Timer Safety: Ensured GuiUpdateTimer stops before processor destruction preventing callback access to destroyed UI components

Stability Improvements

  • Universal DAW Compatibility: VST plugin UI can now be safely closed in all DAW environments without causing host crashes
  • Proper Resource Cleanup: All GUI-related timers and callbacks are properly cleaned up during component destruction
  • Race Condition Prevention: Eliminated timing-dependent crashes between background timer execution and UI component destruction

[v0.9.14] – 2025-09-16

Reference: Oscilloscope performance optimizations
Summary: Optimised oscilloscope rendering performance by adapting sample count to display width and pre-calculating coordinate transformations

Enhanced

  • Adaptive Sample Rendering: Changed oscilloscope to render samples equal to display width instead of fixed 1024 samples, creating optimal 1:1 pixel-to-sample ratio
  • Pre-calculated Transformations: Eliminated repeated coordinate calculations in render loop by pre-calculating X and Y scaling factors
  • Reduced Processing Overhead: Narrow oscilloscope displays now process fewer samples, improving performance when oscilloscope width is less than 1024 pixels
  • Optimised Coordinate Calculations: Combined Y-coordinate calculation with bounds clamping in single operation, removing redundant arithmetic

Technical Implementation

  • OscilloscopeComponent.cpp:86: Changed samplesToRender from fixed 1024 to std::min(static_cast<size_t>(width), audioBuffer.size())
  • Coordinate Pre-calculation: Added xScale and yScale variables calculated once before render loop
  • Loop Optimisation: Replaced division and multiple multiplications with simple multiplication using pre-calculated scale factors

Performance Benefits

  • Variable Processing Load: Oscilloscope CPU usage now scales with display width – smaller displays use less processing power
  • Eliminated Redundant Math: Pre-calculated scaling factors remove repeated arithmetic operations from per-sample calculations
  • Better Cache Efficiency: Reduced calculations per sample improve CPU cache utilization during waveform rendering

[v0.9.13] – 2025-09-16

Reference: Critical audio processing performance optimizations
Summary: Eliminated memory allocations in audio thread and optimised voice rendering for real-time audio performance

Enhanced

  • Real-Time Audio Safety: Fixed critical buffer resize bug in SIDEngine.cpp:84-87 that caused memory allocations during audio processing, replaced with pre-allocated buffer clamping
  • Branch-Free Voice Rendering: Optimised renderAllActiveVoices() in PluginProcessor.cpp:476-505 by building active voice pointer array eliminating conditional branches in rendering loop
  • Eliminated Audio Thread Allocations: Replaced runtime tempBuffer.resize() with compile-time buffer size validation ensuring zero memory allocations in real-time audio path
  • Performance-Critical Optimizations: Focused on hot path audio processing code eliminating function call overhead and branch prediction penalties

Technical Implementation

  • SIDEngine.cpp: Changed dynamic buffer resizing to fixed buffer size validation with sample count clamping preventing audio thread memory allocation
  • PluginProcessor.cpp: Replaced boolean array iteration with direct active voice pointer array for branch-free rendering with improved cache efficiency
  • Voice Processing Architecture: Streamlined active voice detection and rendering pipeline eliminating redundant conditional checks in audio callback

Performance Benefits

  • Zero Audio Thread Allocations: Eliminated all potential memory allocations during real-time audio processing ensuring glitch-free audio output
  • Improved Branch Prediction: Removed conditional branches from voice rendering hot path improving CPU pipeline efficiency and reducing audio latency
  • Better Cache Utilization: Active voice pointer array provides sequential memory access pattern improving CPU cache performance during voice rendering
  • Reduced Function Call Overhead: Eliminated unnecessary function calls and conditional logic in audio processing pipeline

Real-Time Audio Compliance

  • Memory Allocation Safety: All audio processing now operates within pre-allocated memory bounds eliminating potential audio dropouts from memory allocations
  • Deterministic Processing: Fixed buffer sizes and branch-free loops provide predictable audio processing timing critical for professional audio applications
  • Optimised Hot Paths: Critical audio rendering code optimised for minimal CPU overhead and maximum throughput ensuring stable real-time performance

[v0.9.12] – 2025-09-16

Reference: Fixed 256-slot patch array system conversion
Summary: Converted patch storage from dynamic vector to fixed 256-slot array system for simplified architecture and predictable memory usage

Enhanced

  • Fixed Array Architecture: Converted std::vector<PatchData> patches to std::array<PatchData, WavetablePlayer::MAX_PATCHES> patches for predictable memory usage and simplified bounds checking
  • Eliminated Dynamic Allocation: Removed all .resize(), .clear(), .push_back(), and .size() operations from patch management eliminating vector reallocation overhead
  • Simplified Bounds Checking: Replaced patches.size() comparisons with constant WavetablePlayer::MAX_PATCHES (256) throughout codebase for cleaner logic
  • Streamlined Serialization: Simplified binary and JSON serialization logic by always handling exactly 256 patch slots eliminating conditional empty/existing patch handling

Technical Implementation

  • PluginProcessor.h/cpp: Changed core data structure declaration and updated all size-related method calls and initialization logic
  • PatchManagerComponent.h/cpp: Updated return types, replaced vector operations with direct array indexing, simplified patch creation logic
  • WavetableSelectorComponent.cpp: Updated bounds checking from dynamic size to fixed constant for patch access validation
  • FileManager.cpp: Simplified serialization by removing vector-specific operations and implementing element-wise copying for array compatibility
  • PluginEditor.cpp: Updated patch access bounds checking to use fixed array size constant

Architecture Benefits

  • Predictable Memory Usage: Always exactly 256 patch slots allocated regardless of actual patch count eliminating memory fragmentation
  • Simplified Code Logic: Removed complex dynamic allocation, bounds checking, and size management code paths throughout patch system
  • Better Performance: Eliminated vector reallocation overhead and memory management complexity during patch operations
  • Cleaner Initialization: All 256 patches always exist with default constructor initialization eliminating edge cases for empty patch arrays

User Experience

  • Consistent Patch Access: All 256 patch slots (0-255) always available for use matching intended design and user expectations
  • Improved Stability: Eliminated potential memory allocation failures and bounds checking edge cases in patch management system
  • Faster Patch Operations: Removed dynamic memory management overhead during patch loading, saving, and switching operations
  • Professional Reliability: Fixed array system provides deterministic behavior and eliminates vector-related memory management issues

Code Quality Improvements

  • Type Safety: Consistent array types throughout codebase eliminating vector/array type conversion issues and compilation errors
  • Reduced Complexity: Simplified patch management logic by removing dynamic allocation concerns and size-dependent code paths
  • Better Maintainability: Fixed array approach eliminates entire class of bugs related to dynamic vector management and memory allocation
  • Framework Alignment: Fixed-size array matches the intended 256-patch design explicitly documented in MAX_PATCHES constant

[v0.9.11] – 2025-09-16

Reference: Audio processing performance optimizations
Summary: Implemented critical audio buffer optimizations eliminating memory allocations and enhancing stereo processing efficiency

Enhanced

  • Pre-Allocated Audio Buffers: Eliminated repeated memory allocations in SIDVoice::render() by pre-allocating tempBuffer as member variable with 8192-sample capacity
  • Batch Voice Processing: Reorganized audio processing into separate phases – wavetable tick updates followed by optimised batch voice rendering with early exit for inactive voices
  • Optimised Stereo Handling: Integrated stereo channel copying into batch voice rendering using JUCE’s SIMD-optimised copyFrom() method eliminating redundant buffer operations
  • Smart Voice Counting: Batch renderer counts active voices upfront to optimise single-voice scenarios and minimize unnecessary processing overhead

Technical Implementation

  • SIDEngine.h Enhancement: Added std::vector<short> tempBuffer member variable to SIDVoice class for persistent audio buffer storage
  • SIDEngine.cpp Optimisation: Modified constructor to pre-allocate 8192-sample buffer and updated render() method to use resize-only-if-needed logic
  • PluginProcessor.cpp Refactoring: Separated voice processing into wavetable tick phase and batch rendering phase through new renderAllActiveVoices() method
  • Stereo Processing Integration: Moved stereo channel copying from main processBlock() into batch renderer with JUCE’s optimised buffer operations

Performance Benefits

  • Eliminated Memory Allocation Overhead: Removed ~777 allocations/second (3 voices × 259 callbacks/second) that occurred during sustained audio generation
  • Reduced Function Call Overhead: Batch processing minimizes repeated active/enabled checks and optimizes voice rendering pipeline
  • SIMD Acceleration: JUCE’s copyFrom() automatically uses vectorized instructions for stereo channel copying when available
  • Improved Cache Locality: Stereo copying occurs immediately after voice rendering while audio data is hot in cache

User Experience

  • Smoother Audio Performance: Significantly reduced audio processing overhead during sustained multi-voice operation
  • Lower CPU Usage: Optimised audio pipeline reduces system load especially during complex wavetable processing scenarios
  • Professional Reliability: Enhanced audio processing stability through elimination of repeated memory operations in critical audio path
  • Maintained Audio Quality: All optimizations preserve identical audio output while improving underlying performance characteristics

Architecture Benefits

  • Memory-Efficient Design: Pre-allocated buffers eliminate garbage collection pressure and memory fragmentation in audio thread
  • Scalable Performance: Performance improvements become more significant with increased voice count and buffer sizes
  • Framework Integration: Leverages JUCE’s highly optimised internal audio buffer handling methods for maximum efficiency
  • Maintainable Code: Clean separation between voice processing phases and optimised stereo handling for better code organization

[v0.9.10] – 2025-09-16

Reference: Oscilloscope timer consolidation and CPU optimisation for wavetable processing
Summary: Consolidated oscilloscope timer with GUI timer, fixed wavetable Loop defaults causing excessive CPU usage, and optimised rendering performance

Enhanced

  • Timer Consolidation: Merged oscilloscope timer with existing 50Hz GUI timer eliminating separate 60 FPS timer and reducing timer overhead
  • CPU Usage Fix: Changed default Loop behavior from enabled to disabled for all wavetables preventing 800+ processing calls per second that caused high CPU usage
  • Oscilloscope Performance: Reduced glow effect from triple-pass to dual-pass rendering (commented out middle glow layer) for improved drawing performance
  • Wavetable Activation Control: Wavetables now only activate when users explicitly enable Loop, eliminating automatic continuous processing of all 15 voice wavetables

Technical Implementation

  • OscilloscopeComponent Timer Removal: Removed separate juce::Timer inheritance and 60 FPS startTimer(16) from oscilloscope component
  • GUI Timer Integration: Added oscilloscopeUpdateCallback to GuiUpdateTimer calling updateDisplay() at 50Hz instead of separate 60 FPS timer
  • WavetablePlayer Default Fix: Modified constructor to set allWavetablesControl[regType][table][0] = false (Loop disabled) by default instead of true
  • Wavetable Start Method: Enhanced start() method to only activate wavetables that have Loop enabled in control flags preventing mass activation
  • Debug Logging Added: Implemented comprehensive logging system to track filter/vol processing frequency and active wavetable counts for performance analysis

Performance Benefits

  • Reduced Timer Overhead: Eliminated redundant 60 FPS timer while maintaining smooth oscilloscope updates at 50Hz
  • Dramatic CPU Reduction: Fixed CPU bottleneck where all 15 voice wavetables (3 voices × 5 register types) were processing continuously at 50Hz = 750 calls/second
  • User-Controlled Processing: Wavetables only run when explicitly enabled by users through Loop buttons instead of running by default
  • Optimised Rendering: Simplified oscilloscope glow effect reduces path stroking operations while maintaining visual quality

User Experience

  • Responsive Performance: Plugin now operates with significantly lower CPU usage eliminating performance issues during normal operation
  • Intentional Wavetable Animation: Users must explicitly enable Loop on wavetables they want to animate instead of all wavetables running automatically
  • Maintained Visual Quality: Oscilloscope retains smooth animation and visual appeal while using fewer system resources
  • Professional Operation: Plugin performs efficiently in professional DAW environments without excessive CPU overhead

Root Cause Resolution

  • Default Wavetable Processing: All wavetables were enabled by default causing continuous 50Hz processing even when not needed
  • Timer Redundancy: Separate oscilloscope timer created unnecessary overhead when GUI timer could handle all UI updates
  • Mass Activation Bug: start() method was force-activating all wavetables on MIDI note-on regardless of Loop control flag settings
  • Performance Analysis: Added logging revealed 15 active wavetables + 1 filter/vol = 800+ processing calls per second causing CPU spikes

[v0.9.9] – 2025-09-15

Reference: MIDI program change independence from GUI components
Summary: Fixed MIDI program change functionality to work without GUI resources, enabling patch switching when plugin window is closed

Enhanced

  • GUI-Independent Program Changes: MIDI program changes now work reliably when VST GUI is closed by calling setCurrentProgram() directly in audio processor
  • Complete Patch Loading: setCurrentProgram() properly loads all patch settings including wavetable indexes, chip model, and MIDI channel configurations
  • DAW Host Notification: Added refreshParameterList() call in setStateInformation() to notify DAW when program names change after patch restoration
  • Early Patch Initialization: Audio processor constructor initializes 256 patches ensuring getNumPrograms() returns correct value during plugin instantiation

Technical Implementation

  • PluginProcessor.cpp Enhancement: Added direct setCurrentProgram(programNumber) call in MIDI program change handling (line 220) bypassing GUI callback dependency
  • Patch Loading Method: Implemented comprehensive loadPatch() method applying wavetable indexes, chip model, MIDI channels, and global filter/volume settings from patch data
  • Binary Format Restoration: Enhanced parseBinaryV3() to restore patches directly to processor when patch manager unavailable during DAW state restoration
  • Program Name Display: Modified getProgramName() to show meaningful patch names with fallback to “Patch N” for empty patches

Architecture Benefits

  • Audio-First Design: Audio processor maintains complete independence from GUI components for all MIDI program change functionality
  • DAW Compatibility: Plugin works correctly in professional DAW workflows where GUI may be closed while audio processing continues
  • Single Source of Truth: Audio processor owns all patch data ensuring availability regardless of GUI state following proper VST3 architecture patterns
  • Host Integration: Proper DAW notification system ensures program lists refresh when patch data changes

User Experience

  • Seamless MIDI Control: MIDI program changes work identically whether plugin GUI is open or closed eliminating workflow disruptions
  • Professional Reliability: Plugin responds to MIDI program changes from external controllers and DAW automation regardless of GUI state
  • Consistent Behavior: All patch settings including wavetable assignments and chip model properly loaded via MIDI program change
  • DAW Integration: Program names display correctly in DAW program change menus after plugin state restoration

Root Cause Resolution

  • GUI Dependency: Previously MIDI program changes only worked through GUI callback preventing operation when plugin window closed
  • Incomplete Loading: Original implementation only set patch index without applying actual patch settings to audio processing
  • Host Communication: Missing DAW notification prevented program name updates after patch data restoration from saved state

[v0.9.8] – 2025-09-15

Reference: 1-based patch indexing for DAW/MIDI compatibility
Summary: Converted patch display and MIDI program change handling from 0-based to 1-based indexing for improved DAW and MIDI controller compatibility

Enhanced

  • Industry Standard Compatibility: MIDI Program Change 0-127 now maps to user-visible Patches 1-128 matching DAW and MIDI hardware expectations
  • Professional User Experience: Patch list displays 1-256 instead of 0-255, eliminating confusion where “first patch” appeared as “Patch 0”
  • MIDI Controller Integration: Program Change 0 now loads “Patch 1”, Program Change 1 loads “Patch 2”, etc., matching industry conventions
  • DAW Workflow Alignment: Patch numbering now matches professional DAW and music hardware standards where programs start at 1

Technical Implementation

  • MIDI Program Change Translation: Enhanced handleMidiProgramChange() to maintain internal 0-based arrays while presenting 1-based numbers to users
  • UI Display Updates: Modified PatchListItem display formatting to show (patchIndex + 1) in patch list (line 953)
  • Current Patch Display: Updated updateCurrentPatchDisplay() to show 1-based patch numbers in “Playing: XXX” status (line 842)
  • Documentation Updates: Revised User Manual examples to show Program Change 0-127 → Patches 1-128 mapping with clear explanations
  • Backward Compatibility: Maintained internal 0-based data structures and file formats ensuring no breaking changes to existing patch files

User Experience Benefits

  • Intuitive Numbering: “Patch 1” is now the first patch, not “Patch 0”, matching user expectations and industry standards
  • MIDI Hardware Compatibility: MIDI controllers displaying “Program 1” now correctly load the first patch instead of causing confusion
  • Professional Workflow: Eliminates cognitive load of converting between 0-based internal numbering and 1-based industry conventions
  • DAW Integration: Seamless integration with DAWs that display program changes as 1-128 in automation lanes and MIDI editors

Architecture Benefits

  • Clean Translation Layer: Internal 0-based arrays maintained for efficiency while providing 1-based user interface consistently
  • No File Format Changes: Existing .reSIDue and .json patch files continue working unchanged with automatic index translation
  • Consistent Implementation: Applied 1-based display conversion uniformly across all UI components and user-facing documentation
  • Industry Alignment: Plugin now follows established audio industry conventions reducing user confusion and improving professional adoption

[v0.9.7] – 2025-09-15

Reference: Centralized patch limit constant refactoring
Summary: Replaced hardcoded 256 patch limit values with centralized WavetablePlayer::MAX_PATCHES constant for improved maintainability and consistency

Enhanced

  • Centralized Patch Limit: Updated MAX_PATCHES constant from 128 to 256 in WavetablePlayer.h to match actual usage throughout codebase
  • Eliminated Hardcoded Values: Replaced all hardcoded 256 values in PatchManagerComponent.cpp and FileManager.cpp with WavetablePlayer::MAX_PATCHES references
  • Consistent Architecture: Applied same centralized constant pattern used for MAX_TABLES to patch management for unified architecture approach
  • Maintainable Code: All patch-related size checks and loops now use single source of truth eliminating maintenance burden of scattered hardcoded values

Technical Implementation

  • WavetablePlayer.h Enhancement: Updated MAX_PATCHES constant from 128 to 256 (line 12) to match current codebase usage
  • PatchManagerComponent.cpp Updates: Replaced 4 hardcoded 256 values with WavetablePlayer::MAX_PATCHES in getNumRows(), listBoxItemClicked(), loadPatch(), and patch vector sizing
  • FileManager.cpp Updates: Replaced 4 hardcoded 256 values with WavetablePlayer::MAX_PATCHES in binary patch serialization, patch count validation, and patch vector allocation
  • Consistent Pattern: Applied same centralized constant approach used successfully for MAX_TABLES to patch management architecture

Architecture Benefits

  • Single Source of Truth: All patch limit references now use centralized constant eliminating inconsistencies and making future changes trivial
  • Scalable Design: Changing patch limit now requires single constant update instead of hunting through multiple files for hardcoded values
  • Code Consistency: Follows established MAX_TABLES pattern providing uniform approach to configurable limits throughout codebase
  • Maintainable Codebase: Reduced maintenance burden with centralized configuration and eliminated risk of missing hardcoded values during future updates

User Experience

  • Invisible Changes: All existing patch management functionality preserved with no workflow changes or visible differences to users
  • Future Flexibility: Architecture now supports easy adjustment of patch limits through single constant modification if needed
  • Consistent Behavior: All patch-related operations use same limit ensuring consistent behavior across all plugin components

[v0.9.6] – 2025-09-15

Reference: Enhanced patch application with wavetable table switching
Summary: Improved applyPatchToUI() to switch wavetable editor tables to match loaded patch indexes for better visual synchronization

Enhanced

  • Complete Table Switching: When applying a patch, all wavetable editors now switch to display the tables referenced by the patch indexes
  • Voice 0 Synchronization: FREQ, PULS, CTRL, A/D, and S/R editors automatically switch to tables used by voice 0 in the loaded patch
  • Global Filter/Vol Switching: Filter/Vol editor switches to the global table index specified in the patch
  • Visual State Consistency: UI immediately reflects which wavetables are actually being used by the loaded patch

Technical Implementation

  • PatchManagerComponent.cpp Enhancement: Updated applyPatchToUI() method (lines 477-515) to include table switching for all wavetable editor components
  • Safe Component Access: Added proper null checking for wavetableEditorComponent and individual wavetable components before switching tables
  • Index Retrieval: Extract wavetable indexes from patch.wavetableIndexes[voice][registerType] and patch.globalFilterVolIndex for accurate table selection
  • Component Method Calls: Uses switchToTable(index) method on each wavetable component type (FreqWavetableComponent, PulsWavetableComponent, etc.)

User Experience

  • Immediate Visual Feedback: Loading a patch immediately shows the correct wavetable data in all editors eliminating confusion about which tables are active
  • Consistent Interface State: All wavetable editors display data that matches the patch’s actual wavetable references
  • Better Workflow Understanding: Users can immediately see which wavetables a patch uses without having to navigate through table indexes manually
  • Professional Polish: Patch loading provides complete visual synchronization between patch data and editor display state

Architecture Benefits

  • State Synchronization: Eliminates discrepancy between loaded patch data and displayed wavetable content
  • Component Integration: Leverages existing switchToTable() methods across all wavetable editor types for consistent behavior
  • Robust Implementation: Comprehensive null checking ensures stable operation during all plugin lifecycle stages
  • Maintainable Code: Clean separation of patch loading logic with proper component method delegation

[v0.9.5] – 2025-09-14

Reference: Legacy method cleanup and code simplification
Summary: Removed unused loadPatchFromFile() method that became redundant after file handling architecture consolidation

Removed

  • Unused Method Elimination: Removed loadPatchFromFile() method from both PatchManagerComponent.h and PatchManagerComponent.cpp since it had no callers in the current codebase
  • Legacy Code Cleanup: Eliminated method that was previously used by Load buttons but became obsolete after v0.8.3 file handling consolidation
  • Redundant Wrapper Removal: Method only called importFromJSON() which is already directly accessible through hamburger menu system

Technical Implementation

  • Header Cleanup: Removed void loadPatchFromFile(); declaration from PatchManagerComponent.h line 121
  • Implementation Removal: Removed complete method implementation from PatchManagerComponent.cpp lines 661-664
  • API Simplification: Eliminated unnecessary indirection layer that provided no additional functionality over direct importFromJSON() access

Architecture Benefits

  • Cleaner Codebase: Removed approximately 4 lines of dead code that served no purpose in current architecture
  • Reduced Maintenance Burden: Fewer unused methods means less code to maintain and review during future development
  • Simplified API Surface: Eliminated redundant method that duplicated existing functionality without adding value
  • Code Consistency: Aligns with architectural changes where Load buttons use file choosers directly instead of wrapper methods

Root Cause Analysis

  • Historical Context: Method was originally used by Load buttons before v0.8.3 file handling consolidation changed to use direct file chooser implementations
  • Architectural Evolution: File handling architecture evolved to use FileManager methods directly, making this wrapper method obsolete
  • Unused Code Accumulation: Method remained in codebase after Load button behavior changed, creating dead code that served no function

User Experience

  • No Impact: Completely invisible change to users as the method was never called in current codebase
  • Maintained Functionality: All existing JSON import functionality preserved through direct importFromJSON() access via hamburger menu
  • Code Quality Improvement: Cleaner codebase contributes to overall plugin reliability and maintainability

[v0.9.4] – 2025-09-14

Reference: Binary format conversion for loadPatchSetFromFile method
Summary: Refactored loadPatchSetFromFile() to use binary .reSIDue format instead of JSON, consolidated file handling architecture, and cleaned up unused methods

Enhanced

  • Binary Format Loading: Converted loadPatchSetFromFile() from JSON parsing to FileManager::loadPatchSetBinary() calls for consistent binary format usage
  • File Reference Update: Changed from getCurrentPatchSetFile() to getPatchSetFile() to use patches.reSIDue binary files instead of patches.json
  • Simplified Loading Logic: Removed complex JSON parsing, version checking, and legacy format support since binary format handles all data uniformly
  • Consistent File Architecture: All automatic patch loading now uses binary format matching the file chooser operations for Load/Save buttons

Removed

  • Unused Method Cleanup: Eliminated getCurrentPatchSetFile() method from both header and implementation files since it’s no longer called anywhere in codebase
  • JSON Parsing Logic: Removed 42 lines of JSON parsing code including version detection, legacy format support, and complex nested data handling
  • Redundant File Operations: Simplified file existence checking by removing JSON string loading and validation since binary format is self-validating

Technical Implementation

  • PatchManagerComponent.cpp Enhancement: Replaced lines 815-857 JSON parsing logic with single fileManager->loadPatchSetBinary() call
  • File Path Simplification: Updated lines 795-813 to use getPatchSetFile() returning patches.reSIDue and removed JSON string loading operations
  • Method Elimination: Removed getCurrentPatchSetFile() declaration from PatchManagerComponent.h line 117 and complete implementation from .cpp lines 640-659
  • Error Handling Preservation: Maintained file existence checking and default patch creation while using binary format for all successful loads

Architecture Benefits

  • Unified File Format: Plugin now consistently uses binary .reSIDue format for both automatic loading and user-initiated file operations
  • Reduced Complexity: Eliminated dual format support complexity by standardizing on binary format throughout the loading architecture
  • Cleaner Codebase: Removed approximately 50 lines of unused code including complex JSON parsing logic and redundant file path methods
  • Performance Improvement: Binary format loading is faster than JSON parsing and provides more reliable data validation

User Experience

  • Invisible Changes: All existing patch loading functionality preserved with no workflow changes or visible differences to users
  • Improved Reliability: Binary format provides more robust loading with built-in data validation and error recovery
  • Consistent File Handling: Automatic startup loading now uses same binary format as Load/Save buttons for unified file management experience
  • Better Performance: Faster plugin initialization due to more efficient binary loading compared to JSON parsing

Root Cause Resolution

  • Format Inconsistency: Resolved mismatch where automatic loading used JSON while user operations used binary format
  • Code Duplication: Eliminated separate file handling paths by consolidating on single binary format approach
  • Unused Code Accumulation: Cleaned up methods that became obsolete after previous architectural changes

[v0.9.3] – 2025-09-13

Reference: WavetableSelectorComponent architecture cleanup and chip model patch integration
Summary: Removed duplicate tableNumbers cache array from WavetableSelectorComponent and fixed chip model selector to update current patch data

Enhanced

  • Eliminated Data Duplication: Removed std::array<std::array<int, 5>, 3> tableNumbers cache array that duplicated wavetable indexes from patch data
  • Single Source of Truth: WavetableSelectorComponent now reads/writes directly from audioProcessor->patches[currentPatchIndex].wavetableIndexes ensuring data consistency
  • Direct Access Architecture: Implemented direct patch data access through audioProcessor pointer eliminating sync issues between cache and authoritative data
  • Maintained API Compatibility: Preserved existing getTableNumber() and setTableNumber() method signatures ensuring all existing callers work unchanged
  • Chip Model Patch Integration: Fixed chip model selector to update current patch’s chipModel field ensuring patch-specific chip model persistence

Technical Implementation

  • PluginProcessor Integration: Added setAudioProcessor() method to WavetableSelectorComponent with proper forward declaration and include structure
  • Method Reimplemantation: Updated getTableNumber() and setTableNumber() to access patch data directly instead of local cache with comprehensive null safety checks
  • Initialization Enhancement: Added refreshFromPatchData() method to update UI from current patch data eliminating hardcoded initialization values
  • Memory Safety: Added comprehensive null pointer validation and bounds checking for robust operation during all plugin lifecycle stages
  • Chip Model Persistence Fix: Enhanced PluginProcessor::setChipModel() to update current patch’s chipModel field alongside global processor state and SID voices

Architecture Benefits

  • No Sync Issues: Eliminated possibility of cache getting out of sync with authoritative patch data by removing intermediate storage layer
  • Cleaner Code: Removed cache management complexity, initialization logic, and redundant synchronization code, simplifying component architecture and reducing maintenance burden
  • Performance Consistency: Direct access eliminates memory overhead of duplicate storage while maintaining same performance characteristics
  • Framework Compliance: Follows “lightweight patch system” design principles with patches containing only indexes, not data copies
  • Eliminated Redundant Operations: Removed approximately 50 lines of unnecessary wavetable index synchronization code across multiple methods

Fixed

  • Redundant Patch Switching Code: Removed legacy wavetable index synchronization code (lines 288-310) in PatchManagerComponent that was redundant with new direct access architecture
  • Redundant Save Synchronization: Removed unnecessary wavetable index copying in saveAllPatchesToFile() method (lines 341-361) since WavetableSelectorComponent now writes directly to patch data
  • Chip Model Patch Persistence: Fixed chip model button not updating current patch’s chipModel field, ensuring chip model changes are saved with patch data

User Experience

  • Invisible Changes: All existing wavetable selector functionality preserved with no workflow changes or visible differences to users
  • Improved Reliability: Eliminates potential for UI display not matching actual patch data due to cache synchronization issues
  • Consistent Behavior: Wavetable index changes immediately reflected in patch data ensuring proper save/load and patch switching behavior
  • Proper Chip Model Persistence: Chip model selection now properly saved with patches and restored during patch loading

[v0.9.2] – 2025-09-13

Reference: Chip model selector relocation and MIDI channel data architecture
Summary: Moved chip model selector to wavetable selector component, enhanced oscilloscope size, and restructured MIDI channel data to be patch-specific

Added

  • Patch-Specific MIDI Channel Storage: MIDI channel settings now stored in PatchData structure ensuring proper save/load with patches
  • Callback System for MIDI Channels: Implemented getter/setter callbacks to connect WavetableSelectorComponent to patch data
  • Processor MIDI Channel Methods: Added setVoiceMidiChannel() and getVoiceMidiChannel() methods to PluginProcessor
  • Enhanced Patch Loading: MIDI channel dropdowns properly update when switching between patches
  • File Format MIDI Channel Support: Added voiceMidiChannel array serialization to both JSON and binary file formats

Enhanced

  • Chip Model Selector Relocation: Moved from WavetableEditorComponent to WavetableSelectorComponent for logical grouping (affects all voices)
  • Optimised Button Positioning: Chip selector positioned inline with Voice 2 row and F/V selector column for better layout organization
  • Transparent Button Background: Chip model button background made transparent when no border is enabled for cleaner visual integration
  • Oscilloscope Size Increase: Increased preferred width from 384px to 480px (25% wider) for better waveform visualization
  • MIDI Indicator Updates: Fixed MIDI indicator shape changes during patch loading to reflect chip model stored in patches
  • Complete File Format Integration: MIDI channel settings fully integrated into patch save/load system with proper defaults

Fixed

  • Patch Loading MIDI Channels: MIDI channel dropdowns now correctly display loaded patch values instead of always showing defaults
  • Callback Chain Integration: Proper callback setup ensures MIDI channel changes are saved to current patch data
  • Data Architecture: MIDI channel data properly integrated into patch structure instead of being component-specific
  • File Persistence: MIDI channel configurations now properly saved and restored across plugin sessions

[v0.9.1] – 2025-09-12

Reference: Centralized colour scheme management system
Summary: Replaced all hardcoded hex ARGB colours with organised, maintainable colour constants system for improved code maintainability

Added

  • C64ColorScheme.h: Comprehensive colour constants file organizing all UI colours by component type and usage
  • Structured Colour Organization: Colours grouped by UI element (TextEditor, Button, ComboBox, PopupMenu, Label, ListBox, Table, MIDI, Oscilloscope, Component)
  • Semantic Colour Names: Self-documenting colour constants like C64Colors::Button::BackgroundDark and C64Colors::Label::TextCyan
  • Centralized Maintenance: Single source of truth for all colour definitions enabling easy theme modifications

Enhanced

  • Code Maintainability: Eliminated 100+ hardcoded colour instances across 9 source files for centralized management
  • Consistency: Unified colour usage prevents accidental colour variations and ensures visual consistency
  • Developer Experience: Clear, readable colour references improve code understanding and modification workflow
  • Theme Flexibility: Structured approach enables future theme variations and dynamic colour switching

Technical Implementation

  • Namespace Organization: Colours organised in logical namespaces (TextEditor, Button, ComboBox, etc.) for clear categorization
  • Type Safety: All colours defined as const juce::Colour constants preventing runtime modifications
  • Include Integration: Added #include "C64ColorScheme.h" to all relevant source files for colour access
  • Complete Coverage: Replaced all hex colour codes (0xff000000 through 0xffffffff) with named constants

Refactored Files

  • WavetableSelectorComponent.cpp: 37 colour replacements for combo boxes, text editors, buttons, and labels
  • WavetableTableComponent.cpp: 33 colour replacements for table components, editors, and UI elements
  • WavetableTableComponent.h: 4 colour replacements for toggle button states and backgrounds
  • MidiIndicatorComponent.cpp: 7 colour replacements for MIDI status indicator colours
  • WavetableEditorComponent.cpp: 3 colour replacements for component backgrounds
  • PatchManagerComponent.cpp: 21 colour replacements for patch management interface elements
  • OscilloscopeComponent.cpp: 9 colour replacements for waveform visualization and grid colours

Colour Categories Organised

  • Background Colours: Black, DarkBlue, MediumBlue, LightBlue variations for different component depths
  • Text Colours: White, LightGrey, Cyan, Yellow, Green for different text roles and voice indicators
  • Accent Colours: Purple, DarkPurple, LightGreen for highlights, focus states, and special effects
  • State Colours: Red, DarkRed, VeryDarkRed, DarkGreen for active/inactive states and current row indicators
  • Control Colours: Various grey shades for buttons, backgrounds, and UI element differentiation

Architecture Benefits

  • Single Source of Truth: All colours defined once in header file eliminating duplication and inconsistencies
  • Easy Modifications: Change entire colour scheme by modifying constants in one file
  • Scalable Design: Organised structure supports future additions of new colours or theme variants
  • Professional Standards: Follows established patterns for colour management in large codebases

[v0.9.0] – 2025-01-15

Summary: Major UI layout redesign with full-width top bar containing logo, MIDI indicator, and hamburger menu


Pre-v0.9.0 Version Summary

[v0.8.18-v0.8.0] – Major Architectural Improvements

  • v0.8.18: Fixed VST3/Standalone pitch discrepancy with dynamic sample rate configuration
  • v0.8.17: Fixed binary patch file compatibility with proper data distribution
  • v0.8.16: Fixed UI scaling issues and font consistency
  • v0.8.15: Replaced text labels with custom image components
  • v0.8.14: Code cleanup – removed unused methods and dead code
  • v0.8.13: Applied C64 font consistency and text field centering
  • v0.8.12: Added patch index bounds validation for stability
  • v0.8.11: Fixed patch list selection synchronization
  • v0.8.10: Moved patch storage to processor for VST compatibility
  • v0.8.9: Reorganized patch data architecture for headless VST operation
  • v0.8.8: Added comprehensive JUCE-based logging system
  • v0.8.7: Fixed 5x UI rendering performance overhead
  • v0.8.6: Enhanced patch manager visual styling with borders and layout
  • v0.8.5: Optimised UI performance by removing excessive repaints
  • v0.8.4: Added ListBox patch navigation with inline editing
  • v0.8.3: Consolidated file handling with binary format and JSON import/export
  • v0.8.2: Fixed patch filename display during initialization
  • v0.8.1: Fixed custom button state refresh when switching wavetable tables
  • v0.8.0: Implemented shared wavetable architecture eliminating memory duplication

[v0.7.9-v0.7.0] – Configuration and UI Enhancements

  • v0.7.9: Global configuration system for OpenGL and UI scaling
  • v0.7.8: Binary patch file system with DAW state integration
  • v0.7.7: VST3 focus handling fix for wavetable navigation
  • v0.7.6: Save-on-close implementation for patch persistence
  • v0.7.5: 2x UI scaling implementation with state persistence
  • v0.7.4: C64 font consistency and interface cleanup
  • v0.7.3: Custom image toggle button for chip model selection
  • v0.7.2: VST3 MIDI input compatibility fix
  • v0.7.1: Critical VST3 stability fix with callback lifecycle management
  • v0.7.0: Interactive tooltip system for wavetable cells

[v0.6.23-v0.6.0] – Wavetable System Development

  • MIDI Program Change Support: Real-time patch switching and DAW automation
  • Gate-Conditional Jump Commands: Enhanced EXEC operations with SID gate checking
  • Decimal Input Support: A/R column input with validation and templates
  • Scalable Architecture: MAX_TABLES constant for centralized wavetable limits
  • Custom UI Components: Hamburger menu, rename/save buttons, patch management
  • Wavetable Control System: Loop/ReTrig/AutoGate flags with JSON persistence
  • Complete Editor Integration: FREQ/PULS/CTRL/AD/SR wavetable GUI system
  • Template-Based Architecture: Code deduplication and type-safe operations

[v0.5.17-v0.5.0] – Interface Polish and Plugin Rename

  • C64-Themed Interface: Consistent styling with popup menus and ComboBox theming
  • Professional Visual Elements: Embedded logo, oscilloscope enhancements, CRT glow effects
  • Advanced Wavetable Editing: Context menus, copy/paste, optimised serialization
  • Hardware Acceleration: Optional OpenGL rendering with state persistence
  • Complete Plugin Rename: “SID6581” to “reSIDue” throughout entire codebase

[v0.4.2-v0.4.0] – Filter/Volume System and Core Features

  • Complete Filter/Volume Implementation: Global wavetable system with 6-column editor
  • Authentic SID Processing: 11-bit filter cutoff control and independent 50Hz timing
  • Logo Integration: PNG display with proper positioning and scaling
  • Button Visibility Fixes: Proper layout for Loop/Retrig controls
  • Expanded GUI: 2100×1000 pixel interface with enhanced patch management

[v0.3.2-v0.1.0] – Foundation Development (Aug 2025)

Major Architectural Achievements:

  • Complete SID Emulation Pipeline: JUCE + reSID integration to audio output
  • Advanced Wavetable System: 32-row step automation for all SID registers (1000 tables per type)
  • Multi-Voice Architecture: 3 independent voices with monophonic MIDI and per-voice control
  • Professional GUI Framework: C64-themed interface with hex input and real-time visualization
  • Sample-Accurate Timing: Authentic 50Hz processing matching C64 hardware (882 samples @ 44.1kHz)
  • Robust Patch Management: JSON save/load with thread-safe operations and comprehensive persistence

Critical Fixes Resolved:

  • Timing Bug Resolution: Fixed unsigned wraparound preventing continuous processing
  • Voice Control Integration: GUI wavetable selector properly controlling per-voice audio
  • Race Condition Elimination: Thread-safe patch loading with audio pause/resume
  • WAIT/LOOP Processing: Fixed infinite loops with proper 3-variable state machine
  • Direct Access Architecture: Zero-latency GUI-audio communication

Technical Foundation:

  • VST3 Build Support: Complete plugin format compatibility with VST2 stub headers
  • Cross-Platform Compatibility: JUCE File class integration and proper path handling
  • Real-Time Visualization: Working oscilloscope with trigger detection and optimised refresh
  • Professional Workflow: Streamlined patch management and comprehensive error handling