summaryrefslogtreecommitdiff
path: root/tools/spectral_editor/CACHING_OPTIMIZATION.md
diff options
context:
space:
mode:
Diffstat (limited to 'tools/spectral_editor/CACHING_OPTIMIZATION.md')
-rw-r--r--tools/spectral_editor/CACHING_OPTIMIZATION.md173
1 files changed, 173 insertions, 0 deletions
diff --git a/tools/spectral_editor/CACHING_OPTIMIZATION.md b/tools/spectral_editor/CACHING_OPTIMIZATION.md
new file mode 100644
index 0000000..fc25543
--- /dev/null
+++ b/tools/spectral_editor/CACHING_OPTIMIZATION.md
@@ -0,0 +1,173 @@
+# Spectral Editor - Caching Optimization
+
+## Problem
+The spectral editor had severe performance issues due to redundant `drawCurveToSpectrogram()` calls:
+- **Main render loop**: Called for every curve on every frame
+- **Audio playback**: Called when generating audio
+- **Mini spectrum viewer**: Called on every update
+
+With multiple curves, this resulted in hundreds of unnecessary spectrogram computations per second.
+
+## Solution
+Implemented object-oriented `Curve` class with intelligent caching:
+
+### New Architecture
+
+**File: `curve.js`**
+- `Curve` class encapsulates all curve logic
+- Maintains cached spectrogram (`cachedSpectrogram`)
+- Dirty flag tracking (`dirty`)
+- Automatic cache invalidation on parameter changes
+
+**Key Methods:**
+- `getSpectrogram()` - Returns cached version or recomputes if dirty
+- `markDirty()` - Invalidates cache (called automatically by setters)
+- `setProfileType()`, `setProfileSigma()`, `setVolume()` - Setter methods that mark dirty
+- `addControlPoint()`, `updateControlPoint()`, `deleteControlPoint()` - Control point management
+- `setDimensions()` - Update DCT size/frame count (invalidates cache)
+
+### Changes to `script.js`
+
+**Curve Creation:**
+```javascript
+// Old:
+const curve = { id: ..., controlPoints: [], profile: {...}, color: ..., volume: ... };
+
+// New:
+const curve = new Curve(id, dctSize, numFrames);
+curve.setColor(color);
+```
+
+**Rendering:**
+```javascript
+// Old:
+const curveSpec = new Float32Array(dctSize * numFrames);
+drawCurveToSpectrogram(curve, curveSpec, dctSize, numFrames);
+
+// New:
+const curveSpec = curve.getSpectrogram(); // Cached!
+```
+
+**Parameter Updates:**
+```javascript
+// Old:
+curve.profile.type = 'gaussian';
+curve.profile.sigma = 30.0;
+
+// New:
+curve.setProfileType('gaussian'); // Automatically marks dirty
+curve.setProfileSigma(30.0); // Automatically marks dirty
+```
+
+**Control Point Updates:**
+```javascript
+// Old:
+curve.controlPoints.push(point);
+curve.controlPoints[idx] = newPoint;
+curve.controlPoints.splice(idx, 1);
+
+// New:
+curve.addControlPoint(point); // Marks dirty
+curve.updateControlPoint(idx, newPoint); // Marks dirty
+curve.deleteControlPoint(idx); // Marks dirty
+```
+
+**Undo/Redo:**
+```javascript
+// Old:
+snapshot.curves = JSON.parse(JSON.stringify(state.curves));
+state.curves = JSON.parse(JSON.stringify(snapshot.curves));
+
+// New:
+snapshot.curves = state.curves.map(c => c.toJSON());
+state.curves = snapshot.curves.map(json =>
+ Curve.fromJSON(json, dctSize, numFrames)
+);
+```
+
+### Removed Code
+- `drawCurveToSpectrogram()` - Moved to Curve.computeSpectrogram()
+- `evaluateBezierLinear()` - Moved to Curve.evaluateBezierLinear()
+- `evaluateProfile()` - Moved to Curve.evaluateProfile()
+
+### Performance Impact
+
+**Before:**
+- Every render frame: N curves × spectrogram computation
+- 60 FPS × 3 curves = 180 spectrogram computations per second
+- Each computation: ~512 frames × 512 bins × profile evaluation = ~260K operations
+
+**After:**
+- Spectrogram computed once when curve changes
+- Subsequent renders: Direct array access (cached)
+- ~99% reduction in computation for static curves
+
+**Example Timeline:**
+1. User adds control point → Curve marked dirty
+2. Next render: `getSpectrogram()` recomputes (cached)
+3. Next 59 frames: `getSpectrogram()` returns cache (instant)
+4. User drags point → Curve marked dirty
+5. Next render: `getSpectrogram()` recomputes (cached)
+6. ... repeat
+
+### Cache Invalidation Triggers
+
+The cache is automatically marked dirty when:
+- Control points added/updated/deleted
+- Profile type changed
+- Profile sigma changed
+- Curve volume changed
+- Dimensions changed (DCT size / frame count)
+
+The cache is **NOT** marked dirty when:
+- Curve color changed (visual only, doesn't affect spectrogram)
+- Curve selected/deselected (UI state)
+
+### Testing Checklist
+
+- [x] Curve creation works
+- [x] Control point manipulation triggers cache invalidation
+- [x] Profile changes trigger cache invalidation
+- [x] Volume changes trigger cache invalidation
+- [x] Rendering uses cached spectrograms
+- [x] Audio playback uses cached spectrograms
+- [x] Mini spectrum viewer uses cached spectrograms
+- [x] Undo/redo properly reconstructs Curve instances
+- [x] Save/load preserves curve data
+
+### File Changes Summary
+
+**New Files:**
+- `curve.js` (280 lines) - Curve class implementation
+
+**Modified Files:**
+- `index.html` - Added `<script src="curve.js"></script>`
+- `script.js` -
+ - Updated curve creation to use `new Curve()`
+ - Updated all curve property access to use setters
+ - Updated rendering to use `curve.getSpectrogram()`
+ - Updated undo/redo to use `toJSON()`/`fromJSON()`
+ - Removed 89 lines of redundant functions
+ - Changed `profile.param1` to `profile.sigma` throughout
+
+**Total Changes:**
+- +280 lines (curve.js)
+- -89 lines (removed functions)
+- ~150 lines modified (refactored calls)
+- Net: +341 lines, significantly improved performance
+
+### Future Enhancements
+
+Potential optimizations:
+- Incremental cache updates (only recompute affected frames when dragging)
+- Shared spectrogram pool (memory optimization for many curves)
+- Web Worker for background spectrogram computation
+- Progressive rendering (render cached curves first, compute dirty ones async)
+
+### Notes
+
+- All existing functionality preserved
+- Zero visual changes to UI
+- Backwards compatible with existing procedural_params.txt format
+- Cache invalidation is conservative (marks dirty on any parameter change)
+- Memory usage: +1 Float32Array per curve (typically ~1-2 MB total)