| Age | Commit message (Collapse) | Author |
|
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.
|
|
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.
|
|
|
|
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.
|
|
|
|
This commit resolves an inconsistency in the documentation by updating the test count in to match the 35/35 passing tests reported in .
|
|
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.
|
|
|
|
|
|
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>
|
|
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>
|
|
- 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>
|
|
|
|
partial (key 3)
handoff(Claude): playhead offset fix for single partial playback
|
|
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
|
|
- 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>
|
|
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>
|
|
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>
|
|
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
|
|
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>
|
|
- 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>
|
|
handoff(Gemini): key bindings updated in app.js and README.md
|
|
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>
|
|
- 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>
|
|
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>
|
|
- 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
|
|
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>
|
|
- 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>
|
|
- 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>
|
|
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>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
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>
|
|
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>
|
|
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>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
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>
|
|
- '+ 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>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
- 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>
|
|
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>
|
|
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>
|
|
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.
|
|
- 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)
|
|
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>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add k1/k2 range sliders for post-synthesis LP/HP filters. Display maps
coefficients to -3dB cutoff frequency using exact IIR formulas; shows
'bypass' at k=1.0. Uses audioBuffer.sampleRate when loaded, falls back
to 44100 Hz.
handoff(Claude): LP k1 and HP k2 sliders added to Synthesis panel with Hz display.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|