summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
16 hoursfeat(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>
17 hoursfix(mq_editor): swap keys 1/2 — 1=original, 2=synth, 3=partialskal
handoff(Gemini): key bindings updated in app.js and README.md
17 hoursfix(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>
18 hoursfeat(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>
18 hoursfeat(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>
18 hoursrefactor(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
18 hoursfeat(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>
18 hoursfix(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>
19 hoursrefactor(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>
20 hoursfeat(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>
21 hoursdocs(mq_editor): document Explore and Contour interactive modesskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
21 hoursfeat(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>
21 hoursfix(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>
21 hoursfeat(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>
21 hoursfeat(mq_editor): add clear all partials buttonskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
22 hoursperf(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>
22 hoursfeat(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>
23 hoursfix(mq_editor): exclude below-keep partials from hit-test selectionskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
23 hoursrefactor(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
23 hoursfix(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
23 hoursfeat(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
23 hoursfeat(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.
23 hoursrefactor(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>
23 hoursfeat(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>
24 hoursfix(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>
25 hoursfeat(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.
26 hoursfeat(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)
27 hoursfeat(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>
27 hoursdocs(mq_editor): document LP/HP post-synthesis filter slidersskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
27 hoursfeat(mq_editor): LP/HP filter sliders with frequency display in Synthesis panelskal
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>
27 hoursfeat(mq_editor): global resonator test-mode checkbox in Synthesis panelskal
Adds "Resonator (all)" checkbox that forces resonator synthesis for all partials without modifying per-partial mode settings. Fixes null deref when partial.resonator is undefined in forceResonator mode. handoff(Gemini): resonator test-mode checkbox added; no per-partial data mutated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
27 hoursfeat(mq_editor): post-synthesis LP/HP filter pair in synthesizeMQskal
Add first-order IIR low-pass (options.k1) and high-pass (options.k2) filters applied globally after the synthesis loop and before normalization. Both are bypassed when the key is absent; LP is applied before HP (bandpass configuration when both active). handoff(Claude): LP/HP filters added to synthesizeMQ, no UI yet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
27 hoursfeat(mq_editor): per-partial two-pole resonator synthesis modeskal
Each partial in the Synth tab now has a Sinusoid/Resonator toggle. Resonator path: y[n] = 2r·cos(ω₀)·y1 − r²·y2 + A(t)·√(1−r²)·noise, coefficients recomputed per-sample from the freq Bezier curve. gainNorm=√(1−r²) normalises steady-state power; gainComp for trim. UI: mode toggle buttons, r + gain jog sliders, RES badge in header. Docs updated in tools/mq_editor/README.md. handoff(Claude): resonator mode complete, coefficients translated from spread params in README, ready for perceptual comparison testing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
28 hoursfeat(mq_editor): f·Power checkbox, deselect on extract, panel refresh after ↵skal
auto-spread - Add 'f·Power' checkbox: weights spectrum by f before peak detection (f·FFT_Power(f)) to accentuate high-frequency peaks; re-runs extraction on toggle - Deselect partial after Extract Partials run - Fix right panel not refreshing after Auto Spread All: re-call editor.onPartialSelect handoff(Claude): mq_editor UX polish Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
28 hoursfeat(mq_editor): E key for extract, style cleanup, doc updateskal
- Map 'E' key to Extract Partials - Move extractBtn inline style to CSS rule #extractBtn - Hoist autoSpreadAllBtn DOM ref to top with other refs - Drop unused PADY local var in _renderAmpEditor - Remove redundant comment before extractBtn listener - README: add E/Esc keys, add editor.js to architecture, mark Phase 3 done handoff(Gemini): mq_editor UX polish + doc consolidated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
28 hoursfeat(mq_editor): jog sliders for synth params, reset partials on WAV load, ↵skal
panel refresh after extract - Clear extractedPartials and editor state when loading a new WAV - After extract, refresh right panels (re-select if index still valid) - Synth fields (decay, jitter, spread) get jog sliders: drag to nudge, spring-back on release - Spread extension limit dashed line: alpha 0.4→0.75, lineWidth 1→1.5, dash [3,4]→[4,3] handoff(Gemini): mq_editor UX polish — jog sliders, WAV reset, panel refresh, spread line visibility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
28 hoursfeat(mq_editor): spread autodetection, 50% drop-off line, mini-spectrum peak fixskal
- autodetectSpread(): measures half-power (-3dB) peak width in spectrogram to infer spread_above/below (replaces near-zero bezier residual approach) - 'Auto' button per partial + 'Auto Spread All' toolbar button - Spread panel inputs now trigger viewer refresh on change - 50% drop-off dotted reference lines on spread band (selected partial only) - Mini-spectrum: use max() instead of mean() over bins per pixel column, fixing high-frequency amplitude mismatch between original and synthesis handoff(Gemini): spread autodetection and mini-spectrum fixes done
29 hoursfeat(mq_editor): tabbed freq/amp/synth panel, spread band viz, UI polishskal
- Freq/Amp curve params now in tabs to save vertical space - Add Synth tab: per-partial decay, jitter, spread_above/below controls - Visualize spread band on selected partial (filled band + dashed bounds) - Larger fonts and right panel (14px body, 260px panel width) - Non-kept partials at 0.12 alpha (was 0.5) - Default threshold -20dB (was -60dB) handoff(Claude): mq_editor UI/UX improvements
31 hoursfeat(mq_editor): partial selection, amp bezier editor, and editor.js refactorskal
- Click-to-select partials on canvas (proximity hit test on bezier) - Right panel: peak freq/amp, time range, freq/amp bezier text inputs, mute/delete - Selected partial renders on top with glow + larger control points - Draggable freq curve control points on main canvas (grab/grabbing cursor) - Amplitude bezier editor: 120px canvas below spectrogram, time-synced with main view zoom/scroll via viewer.onRender callback - Amp edits live-affect synthesis (mq_synth.js already uses ampCurve) - PartialEditor class in editor.js owns all editing logic; index.html wires it with 5 calls (setViewer, setPartials, onPartialSelect, onRender, onPartialDeleted) handoff(Gemini): partial editing MVP complete. Next: freq curve drag polish or export (.spec generation). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
32 hoursdocs(mq_editor): update README to reflect current stateskal
- Document UI layout (right panel, mini-spectrum color coding) - Add keyboard shortcuts table - Document viewer.js coordinate API - Update algorithm section (backward expansion pass) - Refresh implementation status checklist handoff(Claude): docs updated
32 hoursrefactor(mq_editor): clean coordinate API and remove UI duplicationskal
viewer.js: - Add timeToX(), freqLogNorm(), normToFreq() coordinate primitives - freqToY/canvasToFreq now delegate to freqLogNorm/normToFreq - normalizeDB() replaces duplicated (magDB-(maxDB-80))/80 formula - partialColor(p) replaces repeated color array - All inline time/freq→pixel math replaced with API calls index.html: - getKeepCount() replaces 3 copies of the same calculation - playAudioBuffer() replaces duplicated playback setup + RAF loop handoff(Claude): refactor complete
32 hoursfeat(mq_editor): UI improvements and partial detection enhancementsskal
- Right panel with synthesis checkboxes (integrate phase, disable jitter, disable spread) - Style file chooser as button; show filename next to page title - Second backward pass in extractPartials to recover partial onsets - Cursor line drawn on overlay canvas (no full redraw on mousemove) handoff(Claude): UI + algo improvements complete
33 hoursfeat(mq_editor): validated dual-sine synthesis pipeline, clean code for real ↵skal
audio - Fix incoherent per-sample jitter/spread: seed by partial index only - Fix || fallback for zero-valued params (use != null checks) - Phase integration: accumulator (2π*f/SR per sample) replaces 2π*f*t - Add 'Integrate phase' checkbox to toggle between modes - Revert Catmull-Rom back to simple bezier (1/3, 2/3 sample points) - Remove all debug logging, clean up trackPartials, fitBezier handoff(Gemini): dual-sine test validates full MQ pipeline (extract→track→synth). Next: real audio loading and partial detection improvements. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
33 hoursfix(mq_editor): mini-spectrum and spectrogram display improvementsskal
- Fix mini-spectrum: log-scale frequency axis, per-pixel bin averaging, red peak bars after extraction - Fix key handlers swallowing digits in input fields - Clamp hop size to min 64 to prevent hang - Store maxDB in STFTCache, use it to normalize both mini-spectrum and main spectrogram (fixes clipping at 0dB for pure tones) - Add Test WAV console validation for 440/660Hz peaks - Grayscale colormap for main spectrogram handoff(Gemini): mq_editor display fixes complete, tests not affected
34 hoursfeat(mq_editor): add Test WAV button for in-browser UI testingskal
Generates 2s of 440+660Hz (A4+E5) at 32kHz without needing a file. Refactors WAV load path into shared loadAudioBuffer() used by both file input and the test button. handoff(Gemini): test button wired, full UI pipeline testable without assets.
34 hourstest(mq_editor): add isolated FFT test page and sine generatorskal
- tools/mq_editor/test_fft.html: browser test for fft.js (12 tests: DC impulse, single tone, STFT magnitude, pairs, triplets) - tools/gen_sine_440.py: generate 1s 440Hz WAV at 32kHz for manual testing handoff(Gemini): FFT isolation tests added, all passing in browser.
34 hoursfeat(mq_editor): log-scale frequency axis with clean freqToY/canvasToFreq APIskal
Replace all inline linear freq→Y math with freqToY()/canvasToFreq() pair. freqStart set to 20Hz (log-safe). Freq axis ticks now log-spaced. handoff(Gemini): log scale done, all render paths use freqToY/canvasToFreq
34 hoursrefactor(mq_editor): store only squared amplitude after FFT, drop re/imskal
handoff(Claude): removed full spectrum storage from STFTCache frames; getFFT() deleted; viewers use squaredAmplitude + 10*log10 directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
34 hoursfix(mq_editor): Catmull-Rom bezier fit, NaN guard, synth FFT toggleskal
- evalBezier: guard dt<=0 to avoid NaN on degenerate curves - fitBezier: replace nearest-neighbor control points with Catmull-Rom tangents (Hermite->Bezier), curve now passes through endpoints - key 'a': toggle mini-spectrum between original and synth FFT handoff(Claude): bezier fix + synth FFT comparison Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
41 hoursfeat(mq_editor): sort partials by amplitude, keep% slider for reconstructionskal
- Sort extracted partials by decreasing peak amplitude - Add Keep% range slider (1-100%) to toolbar - Viewer draws omitted partials at 50% opacity (live on slider input) - synthesizeMQ uses only the top-N% partials handoff(Claude): partial amplitude filtering complete