summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-02-21test: fix test_effect_base intermittent crashes and SIGTRAPskal
- Fix stack-use-after-scope dangling reference by storing mutable GpuContext in WebGPUTestFixture. - Fix wgpuDevicePoll causing SIGTRAP and replace with target.read_pixels() to block for GPU teardown safely. - Fix WGPUCommandEncoder leak.
2026-02-21refactor(wgsl): Use vec*f alias for vector typesskal
Replaces all instances of `vec<f32>` with the more concise `vec*f` alias (e.g., `vec3f`) across all `.wgsl` shaders. This improves readability and aligns with common graphics programming conventions. Also adds a new coding style rule to `doc/CODING_STYLE.md` to enforce this standard going forward. Finally, this commit fixes a build error in `test_effect_base.cc` by replacing a call to the non-existent `wgpuDeviceTick` with `wgpuDevicePoll`, which resolves the test failure.
2026-02-21fix(tests): Resolve intermittent SIGTRAP in test_effect_baseskal
The test `test_sequence_render` was disabled due to an intermittent SIGTRAP. The issue was caused by the test application exiting before the GPU finished rendering. This commit fixes the issue by adding a call to `wgpuDeviceTick()` after submitting the command buffer. This ensures that the GPU has completed its work before the test finishes. The test is now re-enabled and passes consistently.
2026-02-21split raymarching.wgsl in two: with id, or without id.skal
2026-02-21refactor(wgsl): Factorize getScreenCoord helperskal
Factorizes the screen coordinate calculation from scene1.wgsl into a reusable getScreenCoord function in common/shaders/math/common_utils.wgsl. This improves code reuse and simplifies fragment shaders.
2026-02-21docs: Proposal for scene1.wgsl SDF helper integrationskal
2026-02-21docs: Update test count in PROJECT_CONTEXT.md to 35/35.skal
This commit resolves an inconsistency in the documentation by updating the test count in to match the 35/35 passing tests reported in .
2026-02-21feat(math): Add vec3::rotate methods for quaternion and axis-angle rotationskal
Adds two convenient methods to the struct: - : Rotates the vector by a given quaternion. - : Rotates the vector around a given axis by an angle in radians. These functions provide a more intuitive and streamlined syntax for vector rotation, improving code readability and usability.
2026-02-21feat: Add vec3::rotate function for quaternion rotation.skal
2026-02-21refine scene1 shaderskal
2026-02-20fix(scene1): refactor render0/render1 to accept Ray structskal
Pass Ray directly instead of separate ro/rd params; minor cleanup (remove redundant consts, cache ray.direction.y). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20feat(scene1): replace ad-hoc camera with CameraParams uniformskal
Build camera via mat4::look_at + inverse in scene1_effect.cc, upload as CameraParams at binding 3. Shader uses getCameraRay() from camera_common. Enable camera_common snippet registration in shaders.cc. handoff(Claude): Scene1 camera now driven by CameraParams uniform; fov=TAU/6 (60° vFOV) matches original tan(PI/3) parameterization. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20feat(sequence): port Scene1Effect + fix seq_compiler absolute time bugskal
- Add Scene1 effect: raymarching cube+sphere+ground (reflections, shadows) - Fix scene1.wgsl: binding 0→2, CommonUniforms→UniformsSequenceParams - Replace Heptagon+Placeholder stub in heptagon_scene with Scene1 - Fix seq_compiler.py: emit seq.start_time+effect.start/end (absolute times) so dispatch_render active check works correctly for all sequences Bug: effects in sequences starting after t=0 were never active because local times (e.g. 0-8) never satisfied params.time<end for absolute time 20+. 34/34 tests passing. handoff(Gemini): seq_compiler now emits absolute effect times. All existing sequences affected — verify visual output across the full timeline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19update tools mq-editor TODOskal
2026-02-19fix(mq_editor): offset playhead to partial t_start when playing single ↵skal
partial (key 3) handoff(Claude): playhead offset fix for single partial playback
2026-02-19fix(mq_editor): remove duplicate mode toggle, add SINE badge, fix ↵skal
mini-spectrum invalidation - Remove redundant synthModeToggle from _updatePropPanel + index.html - Add SINE badge (grey) to panel title, matching existing RES badge (blue) - Invalidate mini-spectrum (viewer.render) on sinusoid/resonator switch handoff(Claude): mq_editor partial panel polish
2026-02-19feat(mq_editor): UI revamp — params panel, layout, partial spectrumskal
- Move Synthesis controls (integratePhase, jitter, spread, resonator, LP/HP filters) and Auto Spread All into the ⚙ Params dropdown - Group Extract Partials / +Partial / ✕ Clear All in one toolbar group - Add per-partial Sine/Res mode toggle in the property panel (Mode row) - Move partial mini-spectrum below the right panel (right-col layout) - Partial mini-spectrum: dynamic dB range scanned across full duration (8 samples, [peak−60, peak]), cached on partial select - Print bezier amplitude A= in red at top-right of partial spectrum - Status/info messages set to 80% gray (#ccc) handoff(Claude): UI revamp complete, TODO items implemented. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19fix(mq_editor): jitter + central spectrum invalidationskal
mq_synth.js: - jitter was only used as a static initial phase offset (inaudible); now drives per-sample LCG frequency perturbation (±jitter fraction of instantaneous freq) in both sinusoidal (integratePhase path) and resonator modes (separate jitterSeed, independent from noise excitation) - disableJitter option now correctly gates jitter to 0 in both modes (was never read before) viewer.js / app.js: - remove invalidatePartialSpectrum() and onResonatorParamChange callback; replace with viewer.onGetSynthOpts callback, called inside _computePartialSpectrum to pull fresh synthOpts at compute time - all UI changes (resonator r/gain, forceResonator, globalR/gain, forceRGain, sinusoidal params) now use viewer.render() as the single invalidation path — no more split between render() and invalidatePartialSpectrum() handoff(Gemini): jitter active on both synth modes; spectrum always sees fresh synthOpts via onGetSynthOpts; viewer.render() is the only invalidation path needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19fix(mq_editor): fuse spread_above/below into single spread paramskal
Asymmetric spread offset the pitch center. Replace with a single symmetric `spread` in harmonics config. autodetectSpread now returns max(above, below). Update all defaults, UI, comments, and README. handoff(Gemini): spread is now a single param; no compat shims. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18fix(mq_editor): partial mini-spectrum — correct FFT, time selection, ↵skal
resonator sync - Fix: fftRadix2 called without bitReversePermute → noisy spectrum (use fftForward) - specTime = mouse pos if inside partial [t0,t3], else center of partial interval - Cache check moved before canvas clear to keep spectrum visible outside [t0,t3] - viewer.synthOpts forwarded to synthesizeMQ so forceResonator/globalR/gain apply - invalidatePartialSpectrum() wired to forceResonator/forceRGain/globalR/globalGain handoff(Gemini): mini-spectrum now correct; fft bug fixed, resonator mode synced
2026-02-18feat(mq_editor): partial spectrum viewer — synth+FFT power displayskal
Adds a 200×100 canvas (left of the main spectrum overlay) that shows the synthesised power spectrum of the selected partial at the time under the mouse (or playhead). Pipeline: synthesizeMQ → Hann window → FFT (2048-pt) → dB power bars. - freqCurve times are shifted so the synthesis window is centred on t - X-axis: log-frequency (same scale as main view) - Y-axis: dB, normalised to peak of the synthesised frame - Cache: {partialIndex, time} → avoids re-synthesis on mouse move; bypassed (force=true) from render() so param changes always redraw handoff(Claude): partial spectrum viewer complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): replace replicas with harmonics modelskal
- Fundamental f0 always synthesized; harmonics added at n*freq_mult - decay^n amplitude rolloff per harmonic (capped at 0.90) - Resonator mode also expanded across harmonics (per-harmonic y1/y2 state) - UI: h.decay, h.freq (default 2.0), jitter, spread↑/↓ params - Viewer: faint dotted harmonic bands with spread visualization - Default freq_mult=2.0 (natural harmonic series) handoff(Gemini): harmonics model complete, ready for next task Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18fix(mq_editor): swap keys 1/2 — 1=original, 2=synth, 3=partialskal
handoff(Gemini): key bindings updated in app.js and README.md
2026-02-18fix(mq_editor): destroy old viewer listeners on WAV reloadskal
Each loadAudioBuffer() was creating a new SpectrogramViewer without removing the previous one's canvas event listeners. Old viewers would fire on every mouse event, rendering stale spectrogram data and calling editor.onPartialSelect() with out-of-range indices (hiding the amp panel). Fix: store handlers as named instance properties, add destroy() to remove them, and call destroy() before creating a new viewer. handoff(Claude): bug fix only, no behaviour change Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): movable inner bezier control points + clamp() refactorskal
- P1/P2 in amp editor now draggable horizontally; t0<t1<t2<t3 enforced - Add clamp() to utils.js; replace all Math.max/min clamping patterns - Cursor hints: move for P1/P2, ns-resize for P0/P3 - Remove test_fft.html - Docs: Delete key, style.css/utils.js in architecture, bezier editor section handoff(Claude): inner control points done, clamp() adopted everywhere Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): group toolbar buttons with separatorsskal
handoff(Claude): toolbar buttons grouped with vertical separators: [Open WAV] | [Extract][AutoSpread] | [Play][Stop] | [+Partial][ClearAll] | [Explore][Contour] | [Undo][Redo] | [Params] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18refactor(mq_editor): extract refreshPartialsView() and playOriginal() helpersskal
- refreshPartialsView(selectIdx) consolidates editor+viewer sync (5 call sites) - playOriginal() replaces inline play button logic; Digit2 calls it directly - autoSpreadAll() now called after extractPartials via named function handoff(Claude): factored common button actions in app.js
2026-02-18feat(mq_editor): add Delete key to remove selected partial; remove Test WAV ↵skal
debug button - 'Delete'/'Backspace' key deletes the currently selected partial - Show 'Del' hint on Delete button in side panel - Remove 'Test WAV' button and validateTestWAVPeaks() debug code Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18fix(mq_editor): key '3' plays partial from t_start; add getAudioBuffer()skal
- synthesizeMQ output trimmed to [t_start-50ms, t_end+50ms] so playback starts immediately at the partial instead of t=0 - Extract synth+trim logic into getAudioBuffer(partials, margin=0) - Stack params vertically in dropdown (grid layout) handoff(Claude): partial playback and CSS param layout fixes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18refactor(mq_editor): extract CSS to style.css, consolidate UI paramsskal
- Move all styles from index.html <style> block to style.css - Merge duplicate :focus rules; add canvas-col, canvas-wrap, amp-edit-header, slider-val classes - Move params (Hop/Threshold/Prominence/Birth/Death/Phase Wt/Min Len) into ⚙ Params dropdown - Relocate Keep slider to bottom-left canvas overlay handoff(Claude): UI consolidation complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): switch curve interpolation to Lagrange through all control ↵skal
points Replace cubic Bezier with Lagrange interpolation so P1/P2 are actual points on the curve. Eval uses stored t1/t2 as arbitrary knot positions. fitBezier keeps least-squares but with Lagrange basis at u=1/3,2/3. Remove endpoint companion drag (no longer tangent handles). TODO: support arbitrary number of inner control points. handoff(Claude): Lagrange interpolation replaces Bezier in mq_editor curve eval/fit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18docs(mq_editor): document Explore and Contour interactive modesskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): add iso-contour tracking mode for bass/diffuse regionsskal
trackIsoContour() follows constant energy level through STFT frames instead of peaks. Useful for broad bass areas where peak detector finds nothing. Preview in cyan, auto-detects spread on commit (naturally large). Toggle: ≋ Contour button or C key. Mutually exclusive with ⊕ Explore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18fix(mq_editor): enable explore mode immediately on WAV loadskal
Pre-compute peak frames after STFT cache is built, so trackFromSeed works without requiring Extract Partials first. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): add explore mode for interactive partial trackingskal
Hover to preview a tracked partial from mouse position (peak-snapped, forward+backward MQ tracking). Click to commit. Toggle with ⊕ Explore button or X key. Escape exits explore mode. handoff(Gemini): explore mode added in mq_extract.trackFromSeed + viewer.js/app.js Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): add clear all partials buttonskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18perf(mq_editor): move playhead to overlay canvas, avoid full render on tickskal
setPlayheadTime() now updates only the playhead overlay and spectrum panel instead of triggering a full renderSpectrogram() every rAF tick. handoff(Gemini): playhead is now an overlay canvas (like cursorCanvas), no more O(n_frames) redraw during playback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): add new partial, undo/redoskal
- '+ Partial' button (N key): insert 440Hz/max-amp partial at front - Undo/Redo buttons (Ctrl+Z/Y): JSON snapshot stack, 50 levels - Hooks in delete, mute, curve edits, amp drag, freq drag handoff(Gemini): undo/redo + new-partial added to mq_editor Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18fix(mq_editor): exclude below-keep partials from hit-test selectionskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18refactor(mq_editor): unify freq+amp into single bezier curveskal
freqCurve now carries a0-a3 (amplitude control values) alongside v0-v3 (frequency). Both components share the same t0-t3 time parameterization. evalBezierAmp() added to utils.js. ampCurve removed from partials and synth pipeline. Amp panel drag now changes only a_i; t is read-only (shared with freq). handoff(Claude): unified freq/amp bezier done
2026-02-18fix(mq_editor): apply coupled anchor drag to freqCurve in viewer canvasskal
Same P0↔P1 / P3↔P2 coupling as the amp editor, now for the frequency bezier dragged in the main spectrogram viewer. handoff(Claude): freq curve coupled drag fixed
2026-02-18feat(mq_editor): drag anchor points P0/P3 with their companion handlesskal
Moving P0 translates P1 rigidly (preserving relative offset). Moving P3 translates P2 rigidly. Handles P1/P2 remain independently draggable. handoff(Claude): bezier coupled anchor drag done
2026-02-18feat(mq_editor): add '3' key to solo selected partial + extract getSynthParams()skal
Extract synth UI params into reusable getSynthParams(). Add Digit3 shortcut to synthesize and play only the currently selected partial. handoff(Claude): getSynthParams() now shared by playSynthesized() and Digit3 handler.
2026-02-18refactor(mq_editor): consolidate duplicates, extract utils.js and app.jsskal
- utils.js (new): evalBezier (robust), getCanvasCoords, buildBandPoints - app.js (new): extract ~450-line inline script from index.html - editor.js: generalize _makeJogSlider(inp, options) with onUpdate cb, eliminate 50-line inline resonator jog duplication, use getCanvasCoords - mq_extract.js: extract findBestPeak(), replace two identical loop bodies - viewer.js: remove duplicate evalBezier, use getCanvasCoords/buildBandPoints - mq_synth.js: remove duplicate evalBezier - index.html: inline script removed, load order: utils→fft→extract→synth→viewer→editor→app handoff(Claude): mq_editor refactor complete — no logic changes, browser-ready. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): expose tracking params in UI with grouped param panelskal
Reorganize extraction parameters into four labeled groups (STFT / Peak Detection / Tracking / Filter). Expose Birth, Death, Phase Wt and Min Len as live controls wired to runExtraction(). All labels carry tooltip descriptions. mq_extract.js now reads these from params instead of hardcoded constants (same defaults preserved). handoff(Claude): tracking params exposed, panel grouped, README updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18fix(mq_editor): correct phaseInterp quadratic coefficient and stale phase ↵skal
prediction - phaseInterp: missing *0.5 on quadratic term made interpolated phase 2x off - trackPartials: phase advance now scaled by (age+1) to cover frames missed during a partial gap, preventing stale predictions after consecutive misses handoff(Claude): two bugs fixed in mq_extract.js phase tracking Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18feat(mq_editor): Implement phase-coherent partial trackingskal
Implements a more robust partial tracking algorithm by using phase coherence to validate and link spectral peaks across frames. This significantly improves tracking accuracy, especially for crossing or closely-spaced partials. Key changes: - : The STFT cache now computes and stores the phase for each frequency bin alongside the magnitude. - : The peak detection logic now interpolates the phase for sub-bin accuracy. - : The core tracking algorithm was replaced with a phase-aware model. It predicts the expected phase for a partial in the next frame and uses a cost function combining both frequency and phase error to find the best match. This implementation follows the design outlined in the new document . handoff(Gemini): Phase prediction implemented. Further tuning of the cost function weights may be beneficial.
2026-02-18feat(mq_editor): implement MQ extraction improvementsskal
- Implement Predictive Kinematic Tracking to improve partial tracking during fast glissandos and vibrato. - Add Peak Prominence Pruning to filter out insignificant local maxima. - Replace heuristic Bezier fitting with a Least-Squares solver for more accurate trajectories. - Update UI to include a Prominence parameter input. - Archive MQ_EXTRACTION_IMPROVEMENTS.md design document. handoff(Gemini): implemented MQ extraction improvements (kinematic tracking, prominence pruning, least-squares bezier)
2026-02-18feat(mq_editor): global r/gain overrides for Resonator (all) modeskal
Add r (pole) and gain sliders with force r/gain checkbox in Synthesis panel, visible when Resonator (all) is active. Restrict r range to [0.75, 0.9999] for both global and per-partial sliders. handoff(Claude): global resonator r/gain override controls added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18docs(mq_editor): document LP/HP post-synthesis filter slidersskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>