Skip to content

Commit 81244c2

Browse files
committed
Add CLI backend and include patch for build fixes
Includes patch file (iPlug2-cli-fixes.patch) with fixes for: - M_PI undefined on MSVC (add _USE_MATH_DEFINES and fallback define) - fopen macro conflict with win32_utf8.h (remove redundant redefinition) To apply the patch to the iPlug2 submodule: cd iPlug2 && git apply ../iPlug2-cli-fixes.patch
1 parent 6e8ca26 commit 81244c2

File tree

10 files changed

+1837
-2
lines changed

10 files changed

+1837
-2
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
---
2+
name: analyze-dsp
3+
description: DSP analysis toolkit for iPlug2 CLI plugins - impulse response, THD, noise floor, validation
4+
---
5+
6+
# DSP Analysis Toolkit
7+
8+
Use this skill to analyze and visualize audio processing from iPlug2 plugins using the CLI target.
9+
10+
## Requirements
11+
12+
- Plugin built with CLI target (`-cli` suffix)
13+
- Python 3 with matplotlib and numpy (`pip install matplotlib numpy`)
14+
15+
## Quick Start
16+
17+
```bash
18+
# Build the CLI target
19+
ninja -C build-ninja MyPlugin-cli
20+
21+
# Basic impulse response
22+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --set 0 100 -o plot.png
23+
24+
# THD analysis (distortion measurement)
25+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --thd 1000 100 -o thd.png
26+
27+
# Validation checks
28+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --validate unity
29+
```
30+
31+
**Important:** Always use the Read tool to show generated plots to the user.
32+
33+
## CLI Test Signals
34+
35+
The CLI binary supports these test signals:
36+
37+
```bash
38+
--impulse <samples> Unit impulse response (default: 4096)
39+
--sine <freq> <ms> Sine wave at frequency for duration
40+
--noise <ms> White noise for duration
41+
--step <ms> Unit step response
42+
--chirp <start> <end> <ms> Log frequency sweep
43+
```
44+
45+
## Python Script Options
46+
47+
### Basic Options
48+
49+
| Option | Description |
50+
|--------|-------------|
51+
| `--set <idx> <val>` | Set parameter by index |
52+
| `--length <n>`, `-l` | Impulse length in samples |
53+
| `--sr <rate>` | Sample rate (default: 44100) |
54+
| `--output <file>`, `-o` | Save plot to PNG |
55+
| `--no-plot` | Print numeric data only |
56+
57+
### Plot Types
58+
59+
| Option | Description |
60+
|--------|-------------|
61+
| `--spectrum`, `-s` | Frequency response (magnitude dB) |
62+
| `--phase` | Phase response |
63+
| `--combined`, `-c` | 4-panel view |
64+
| `--group-delay` | Group delay vs frequency |
65+
66+
### Analysis Modes
67+
68+
| Option | Description |
69+
|--------|-------------|
70+
| `--thd <freq> <ms>` | THD analysis with sine input |
71+
| `--noise-floor <ms>` | Noise floor from silence |
72+
| `--step-response <ms>` | Step response metrics |
73+
| `--validate <type>` | Sanity checks (effect/unity/lowpass/highpass) |
74+
| `--metrics <file>` | Export metrics as JSON |
75+
76+
### Instrument/Synth Mode
77+
78+
| Option | Description |
79+
|--------|-------------|
80+
| `--note <midi>` | Render MIDI note |
81+
| `--velocity <vel>` | Note velocity (default: 100) |
82+
| `--duration <ms>` | Note duration (default: 500) |
83+
| `--release <ms>` | Release time (default: 500) |
84+
85+
## Examples
86+
87+
### THD Analysis
88+
89+
Measure harmonic distortion with a 1kHz sine wave:
90+
91+
```bash
92+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --set 0 100 --thd 1000 100 -o thd.png
93+
```
94+
95+
Output shows THD percentage and harmonic levels relative to fundamental.
96+
97+
### Validation Checks
98+
99+
Run sanity checks on impulse response:
100+
101+
```bash
102+
# Basic checks (NaN, peak, DC, energy decay)
103+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --validate effect
104+
105+
# Unity gain validation (for gain/bypass plugins)
106+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --set 0 100 --validate unity
107+
108+
# Lowpass filter validation
109+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --validate lowpass
110+
```
111+
112+
### Noise Floor Analysis
113+
114+
Measure output noise from silence input:
115+
116+
```bash
117+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --noise-floor 1000
118+
```
119+
120+
Reports RMS, peak, crest factor, and estimated bit resolution.
121+
122+
### Step Response
123+
124+
Analyze transient behavior:
125+
126+
```bash
127+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --step-response 100 -o step.png
128+
```
129+
130+
Reports rise time, overshoot, and settling time.
131+
132+
### JSON Metrics Export
133+
134+
Export comprehensive metrics for automation:
135+
136+
```bash
137+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --no-plot --metrics output.json
138+
```
139+
140+
### Instrument/Synth Analysis
141+
142+
Render and analyze MIDI notes:
143+
144+
```bash
145+
# Render middle C (note 60)
146+
python3 scripts/cli_analyze.py ./build-ninja/out/MySynth --note 60 --combined -o note60.png
147+
148+
# With custom envelope
149+
python3 scripts/cli_analyze.py ./build-ninja/out/MySynth --note 60 --duration 200 --release 800 -o note.png
150+
```
151+
152+
### Parameter Sweep
153+
154+
Compare different parameter values:
155+
156+
```bash
157+
# Time domain comparison
158+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --sweep 0 0 100 4 -o sweep.png
159+
160+
# Frequency domain comparison
161+
python3 scripts/cli_analyze.py ./build-ninja/out/MyPlugin --sweep 0 0 100 4 --spectrum -o freq_sweep.png
162+
```
163+
164+
## DSP Iteration Workflow
165+
166+
1. Make changes to plugin DSP code
167+
2. Rebuild: `ninja -C build-ninja MyPlugin-cli`
168+
3. Run analysis:
169+
- `--thd 1000 100` for distortion
170+
- `--validate unity` for sanity checks
171+
- `--combined` for full visualization
172+
4. Read plot images to show user
173+
5. Iterate based on results
174+
175+
## Direct CLI Usage
176+
177+
The analysis script wraps the CLI binary. Direct usage:
178+
179+
```bash
180+
# Impulse response as text
181+
./build-ninja/out/MyPlugin --set 0 100 --impulse 1024 --output-txt ir.txt
182+
183+
# Sine wave response
184+
./build-ninja/out/MyPlugin --set 0 100 --sine 1000 100 --output-txt sine.txt
185+
186+
# MIDI note render
187+
./build-ninja/out/MyPlugin --midi 60 100 0 500 --render 1000 --output render.wav
188+
```

.claude/skills/screenshot/SKILL.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
name: screenshot
3+
description: Take a screenshot of an iPlug2 standalone app's GUI for iteration with AI
4+
---
5+
6+
# Screenshot iPlug2 App GUI
7+
8+
Use this skill to capture a screenshot of a running iPlug2 standalone app. This is useful for iterating on GUI design with AI assistance.
9+
10+
## Requirements
11+
12+
- The app must be built (any configuration works for CLI method)
13+
- For keyboard/menu methods: the app must be running in **Debug** configuration
14+
- On first use, macOS may prompt for Screen Recording permission (non-IGraphics builds only)
15+
16+
## Taking a Screenshot
17+
18+
### Method 1: Command Line (Recommended for automation)
19+
Run the app with `--screenshot <path>`:
20+
```bash
21+
./build-ninja/out/TemplateProject.app/Contents/MacOS/TemplateProject --screenshot screenshot.png
22+
```
23+
24+
The app will launch, render the GUI, save the screenshot, and exit automatically. This method:
25+
- Skips audio/MIDI initialization for faster startup
26+
- Works headlessly (no user interaction required)
27+
- Is ideal for CI/CD and AI-assisted iteration
28+
29+
### Method 2: Keyboard Shortcut
30+
With the app in focus, press:
31+
- **macOS:** `Cmd+Shift+S`
32+
- **Windows:** `Ctrl+Shift+S`
33+
34+
### Method 3: Menu
35+
1. Open the **Debug** menu
36+
2. Click **Save Screenshot**
37+
38+
## Screenshot Location
39+
40+
Screenshots are saved to the system temp directory with a timestamp:
41+
```
42+
{TMPDIR}/{PluginName}_screenshot_YYYYMMDD_HHMMSS.png
43+
```
44+
45+
On macOS, `TMPDIR` is typically `/var/folders/.../T/`
46+
47+
After saving, a dialog appears with the full path and an option to open the file.
48+
49+
## Viewing Screenshots in Claude Code
50+
51+
When using the CLI method, the screenshot is saved directly to the specified path:
52+
```bash
53+
./MyPlugin.app/Contents/MacOS/MyPlugin --screenshot screenshot.png
54+
# Then read screenshot.png
55+
```
56+
57+
For interactive screenshots (keyboard/menu), find the most recent one:
58+
```bash
59+
ls -t $TMPDIR/*_screenshot_*.png | head -1
60+
```
61+
62+
Then ask Claude Code to read the screenshot file.
63+
64+
## Troubleshooting
65+
66+
### No Debug menu visible
67+
- Ensure the app is built with **Debug** configuration
68+
- For CMake builds: `cmake -B build -DCMAKE_BUILD_TYPE=Debug`
69+
- For Xcode builds: Select "Debug" configuration
70+
71+
### Screenshot is blank
72+
- This can happen with certain GPU-accelerated views
73+
- The IGraphics-based screenshot (used when IGraphics is available) should work with Metal/NanoVG/Skia backends
74+
75+
### Permission denied on macOS
76+
- Go to System Preferences > Privacy & Security > Screen Recording
77+
- Enable permission for the app or Terminal
78+
79+
## Technical Details
80+
81+
The screenshot feature uses:
82+
- **IGraphics builds:** Layer-based capture via `IGraphics::SaveScreenshot()` - works with all backends
83+
- **Non-IGraphics builds (Visage, SwiftUI, etc.):** `CGWindowListCreateImage` API via dlsym
84+
85+
Screenshots are captured at full Retina/HiDPI resolution.

TemplateProject/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ iplug_add_plugin(${PROJECT_NAME}
1414
RESOURCES
1515
resources/fonts/Roboto-Regular.ttf
1616
FORMATS
17-
MINIMAL_PLUGINS APP
17+
MINIMAL_PLUGINS APP CLI
1818
)

TemplateProject/resources/main.rc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ BEGIN
9090
MENUITEM "&Show Control Bounds\tCtrl+B", ID_SHOW_BOUNDS
9191
MENUITEM "&Show Drawn Area\tCtrl+D", ID_SHOW_DRAWN
9292
MENUITEM "&Show FPS\tCtrl+F", ID_SHOW_FPS
93+
MENUITEM SEPARATOR
94+
MENUITEM "&Save Screenshot\tCtrl+Shift+S", ID_SCREENSHOT
9395
END
9496
POPUP "&Help"
9597
BEGIN
@@ -176,6 +178,7 @@ BEGIN
176178
"D", ID_SHOW_DRAWN, VIRTKEY, CONTROL, NOINVERT
177179
"F", ID_SHOW_FPS, VIRTKEY, CONTROL, NOINVERT
178180
"E", ID_LIVE_EDIT, VIRTKEY, CONTROL, NOINVERT
181+
"S", ID_SCREENSHOT, VIRTKEY, CONTROL, SHIFT, NOINVERT
179182
END
180183

181184
/////////////////////////////////////////////////////////////////////////////

TemplateProject/resources/main.rc_mac_menu

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ SWELL_DEFINE_MENU_RESOURCE_BEGIN(IDR_MENU1)
1010
MENUITEM "&Show Control Bounds\tCtrl+B", ID_SHOW_BOUNDS
1111
MENUITEM "&Show Drawn Area\tCtrl+D", ID_SHOW_DRAWN
1212
MENUITEM "&Show FPS\tCtrl+F", ID_SHOW_FPS
13+
MENUITEM SEPARATOR
14+
MENUITEM "&Save Screenshot\tCtrl+Shift+S", ID_SCREENSHOT
1315
END
1416
POPUP "&Help"
1517
BEGIN

TemplateProject/resources/resource.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define ID_SHOW_DRAWN 40026
3232
#define ID_SHOW_FPS 40027
3333
#define ID_SHOW_BOUNDS 40028
34+
#define ID_SCREENSHOT 40029
3435

3536
// Next default values for new objects
3637
//

0 commit comments

Comments
 (0)