diff options
Diffstat (limited to 'tools/spectral_editor/CACHING_OPTIMIZATION.md')
| -rw-r--r-- | tools/spectral_editor/CACHING_OPTIMIZATION.md | 173 |
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) |
