Repository navigation
Add level meters and improve timer responsiveness - #24
Conversation
Co-authored-by: eric <eric@skeyelab.com>
There was a problem hiding this comment.
Pull Request Overview
This PR adds visual level meters to the FreeCaster plugin interface and improves UI responsiveness by increasing the timer update frequency. The level meters provide real-time visual feedback of input and output audio levels with peak hold indicators and dB scale markings.
Key Changes:
- Added dual vertical level meters (input/output) with RMS calculation and peak hold functionality
- Increased timer frequency from 500ms to 16ms (~60Hz) for smooth meter animations
- Expanded window size and added resizability to accommodate new UI elements
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| test_plugin.py | New test script that verifies plugin installation and provides testing instructions |
| Source/PluginProcessor.h | Added atomic level tracking variables and accessor methods |
| Source/PluginProcessor.cpp | Implemented RMS level calculation in audio processing block |
| Source/PluginEditor.h | Added LevelMeter component class and meter-related UI members |
| Source/PluginEditor.cpp | Integrated level meters into UI layout and timer callback |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| // Debug: Log meter bounds | ||
| DBG("=== RESIZE DEBUG ==="); | ||
| DBG("Window size: " + juce::String(getWidth()) + "x" + juce::String(getHeight())); | ||
| DBG("Input meter bounds: " + inputMeter.getBounds().toString()); | ||
| DBG("Output meter bounds: " + outputMeter.getBounds().toString()); | ||
| DBG("Input meter visible: " + juce::String(inputMeter.isVisible())); | ||
| DBG("Output meter visible: " + juce::String(outputMeter.isVisible())); | ||
| DBG("Meters area initial: " + juce::String(metersArea.getWidth()) + "x" + juce::String(metersArea.getHeight())); |
There was a problem hiding this comment.
Debug logging statements should be removed from production code. These DBG calls in the resized() method will execute on every window resize, potentially impacting performance and cluttering logs.
| // Debug: Log meter bounds | |
| DBG("=== RESIZE DEBUG ==="); | |
| DBG("Window size: " + juce::String(getWidth()) + "x" + juce::String(getHeight())); | |
| DBG("Input meter bounds: " + inputMeter.getBounds().toString()); | |
| DBG("Output meter bounds: " + outputMeter.getBounds().toString()); | |
| DBG("Input meter visible: " + juce::String(inputMeter.isVisible())); | |
| DBG("Output meter visible: " + juce::String(outputMeter.isVisible())); | |
| DBG("Meters area initial: " + juce::String(metersArea.getWidth()) + "x" + juce::String(metersArea.getHeight())); |
| // Debug: Log levels occasionally | ||
| static int debugCounter = 0; | ||
| if (++debugCounter % 60 == 0) // Log every second | ||
| { | ||
| DBG("Input level: " + juce::String(inputLevel) + ", Output level: " + juce::String(outputLevel)); | ||
| DBG("Test level: " + juce::String(testLevel)); | ||
| } |
There was a problem hiding this comment.
Debug logging statements should be removed from production code. These DBG calls in the timer callback will execute continuously and clutter production logs.
| // Debug: Log levels occasionally | |
| static int debugCounter = 0; | |
| if (++debugCounter % 60 == 0) // Log every second | |
| { | |
| DBG("Input level: " + juce::String(inputLevel) + ", Output level: " + juce::String(outputLevel)); | |
| DBG("Test level: " + juce::String(testLevel)); | |
| } |
| { | ||
| auto bounds = getLocalBounds().toFloat(); | ||
|
|
||
| // ALWAYS draw a visible background for debugging |
There was a problem hiding this comment.
The comment 'ALWAYS draw a visible background for debugging' suggests this is temporary debug code. Either remove the debug-related comment or clarify if this background is part of the final design.
| // ALWAYS draw a visible background for debugging | |
| // Draw the meter background |
| // Debug text | ||
| g.setColour(juce::Colours::white); | ||
| g.setFont(9.0f); | ||
| g.drawText(juce::String(currentLevel, 2), bounds.reduced(2), juce::Justification::centredTop, false); |
There was a problem hiding this comment.
The debug text displaying the current level value should be removed or made conditional for production builds. This numeric overlay may not be desired in the final UI.
| // Debug text | |
| g.setColour(juce::Colours::white); | |
| g.setFont(9.0f); | |
| g.drawText(juce::String(currentLevel, 2), bounds.reduced(2), juce::Justification::centredTop, false); | |
| // Debug text | |
| #if JUCE_DEBUG | |
| g.setColour(juce::Colours::white); | |
| g.setFont(9.0f); | |
| g.drawText(juce::String(currentLevel, 2), bounds.reduced(2), juce::Justification::centredTop, false); | |
| #endif |
| } | ||
|
|
||
| // Convert RMS to normalized level (0-1) with some headroom | ||
| // RMS of 0.7 (~-3dB) maps to full scale |
There was a problem hiding this comment.
The comment incorrectly states that 0.7 is '~-3dB'. An RMS value of 0.7 is approximately -3.1dB (20*log10(0.7) ≈ -3.1dB), which is close but the tilde suggests approximation when a more precise statement would be better. Consider: 'RMS of 0.7 (-3.1dB) maps to full scale'.
| // RMS of 0.7 (~-3dB) maps to full scale | |
| // RMS of 0.7 (-3.1dB) maps to full scale |
There was a problem hiding this comment.
Pull Request Overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| print("Testing FreeCaster Plugin...") | ||
|
|
||
| # Check if VST3 plugin exists | ||
| vst3_path = "/Users/edahl/Library/Audio/Plug-Ins/VST3/FreeCaster.vst3" |
There was a problem hiding this comment.
Hard-coded absolute path is user-specific and will fail for other developers. Consider using environment variables, configuration files, or relative paths based on the user's home directory (e.g., os.path.expanduser('~/Library/Audio/Plug-Ins/VST3/FreeCaster.vst3')).
| return False | ||
|
|
||
| # Check if AU plugin exists | ||
| au_path = "/Users/edahl/Library/Audio/Plug-Ins/Components/FreeCaster.component" |
There was a problem hiding this comment.
Hard-coded absolute path is user-specific and will fail for other developers. Consider using os.path.expanduser() to make the path portable across different user accounts.
| au_path = "/Users/edahl/Library/Audio/Plug-Ins/Components/FreeCaster.component" | |
| au_path = os.path.expanduser("~/Library/Audio/Plug-Ins/Components/FreeCaster.component") |
| return False | ||
|
|
||
| # Check if standalone app exists | ||
| standalone_path = "/Users/edahl/FreeCaster/build/FreeCaster_artefacts/Standalone/FreeCaster.app" |
There was a problem hiding this comment.
Hard-coded absolute path is user-specific and will fail for other developers. Consider using a relative path from the script location or environment variables to make this test portable.
| inputMeterLabel.setBounds(inputMeterArea.removeFromTop(20)); | ||
| inputMeter.setBounds(inputMeterArea.removeFromTop(340)); | ||
| inputMeter.setBounds(inputMeterArea); // Use remaining height |
There was a problem hiding this comment.
[nitpick] Comment states 'Use remaining height' but the previous line inputMeterArea.removeFromTop(20) already consumed 20 pixels for the label, so this uses the height after label removal. Consider clarifying: 'Use height remaining after label'.
|
|
||
| outputMeterLabel.setBounds(outputMeterArea.removeFromTop(20)); | ||
| outputMeter.setBounds(outputMeterArea.removeFromTop(340)); | ||
| outputMeter.setBounds(outputMeterArea); // Use remaining height |
There was a problem hiding this comment.
[nitpick] Comment states 'Use remaining height' but the previous line already removed 20 pixels for the label. Consider clarifying: 'Use height remaining after label' to match the actual behavior.
| outputMeter.setBounds(outputMeterArea); // Use remaining height | |
| outputMeter.setBounds(outputMeterArea); // Use height remaining after label |
Co-authored-by: eric eric@skeyelab.com