summaryrefslogtreecommitdiff
path: root/tools/mq_editor
AgeCommit message (Collapse)Author
81 min.feat(mq_editor): switch curve interpolation to Lagrange through all control ↵HEADmainskal
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>
110 min.docs(mq_editor): document Explore and Contour interactive modesskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
113 min.feat(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>
2 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>
2 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>
2 hoursfeat(mq_editor): add clear all partials buttonskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 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>
3 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>
4 hoursfix(mq_editor): exclude below-keep partials from hit-test selectionskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 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
4 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
4 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
4 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.
4 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>
4 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>
4 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>
6 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.
7 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)
8 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>
8 hoursdocs(mq_editor): document LP/HP post-synthesis filter slidersskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8 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>
8 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>
8 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>
8 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>
9 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>
9 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>
9 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>
9 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
10 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
12 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>
13 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
13 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
13 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
14 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>
14 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
14 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.
14 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.
15 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
15 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>
15 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>
22 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
23 hoursfeat(mq_editor): peaks display, STFT cache refactor, threshold reactivityskal
- STFTCache: cache squaredAmplitude (re²+im²) per frame, add getSquaredAmplitude(t) - getMagnitudeDB uses cached sq amp (10*log10, no sqrt) - detectPeaks uses squaredAmp from cache instead of recomputing FFT - extractPartials: use cache frames, return {partials, frames} - Press 'p' to toggle raw peak overlay in viewer - threshold input: step=any, change event triggers re-extraction - runExtraction() shared by button and threshold change handoff(Claude): mq_partial peaks/cache refactor complete
23 hoursclean-up pass mq-partialsskal
23 hoursfeat(mq_editor): Complete Phase 2 - JS synthesizer with STFT cacheskal
Phase 2 - JS Synthesizer: - Created mq_synth.js with replica oscillator bank - Bezier curve evaluation (cubic De Casteljau algorithm) - Replica synthesis: frequency spread, amplitude decay, phase jitter - PCM buffer generation from extracted MQ partials - Normalization to prevent clipping - Key '1' plays synthesized audio, key '2' plays original - Playback comparison with animated playhead STFT Cache Optimization: - Created STFTCache class in fft.js for pre-computed windowed FFT frames - Clean interface: getFFT(t), getMagnitudeDB(t, freq), setHopSize() - Pre-computes all frames on WAV load (eliminates redundant FFT calls) - Dynamic cache update when hop size changes - Shared across spectrogram, tooltip, and mini-spectrum viewer - Significant performance improvement Mini-Spectrum Viewer: - Bottom-right overlay (200x100) matching spectral_editor style - Real-time FFT display at playhead or mouse position - 100-bar visualization with cyan-to-yellow gradient - Updates during playback or mouse hover Files: - tools/mq_editor/mq_synth.js (new) - tools/mq_editor/fft.js (STFTCache class added) - tools/mq_editor/index.html (synthesis playback, cache integration) - tools/mq_editor/viewer.js (cache-based rendering, spectrum viewer) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
24 hoursdocs(mq_editor): Update Phase 1 completion statusskal
Phase 1 deliverables complete: - MQ extraction with improved tracking - Spectrogram visualization with zoom/scroll - Original WAV playback with playhead - Ready for Phase 2 (JS synthesizer) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
24 hoursfeat(mq_editor): Improve partial tracking and add audio playbackskal
Tracking improvements: - Frequency-dependent threshold (5% of freq, min 20 Hz) - Candidate system requiring 3-frame persistence before birth - Extended death tolerance (5 frames) for robust trajectories - Minimum 10-frame length filter for valid partials - Result: cleaner, less scattered partial trajectories Audio playback: - Web Audio API integration for original WAV playback - Play/Stop buttons with proper state management - Animated red playhead bar during playback - Keyboard shortcuts: '2' plays original, '1' reserved for synthesis Visualization: - Power law (gamma=0.3) for improved spectrogram contrast Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
25 hoursfix(mq_editor): Improve spectrogram visualization and navigationskal
- Fixed FFT to 1024 bins for 31.25 Hz resolution (better bass analysis) - Refactored view state to zoom_factor + t_center for cleaner pan/zoom - Mousewheel scrolls horizontally, shift+mousewheel zooms (respects deltaX/Y) - Spectrogram bins now fill complete time/freq buckets at all zoom levels - Extended dB range to -80→0 dB (80 dB) for better high-amplitude granularity - Added real-time intensity tooltip in dB - 50% alpha on spectrogram to reduce clutter over partial trajectories Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
28 hoursfeat(mq_editor): Phase 1 - MQ extraction and visualization (SPECTRAL_BRUSH_2)skal
Implement McAulay-Quatieri sinusoidal analysis tool for audio compression. New files: - doc/SPECTRAL_BRUSH_2.md: Complete design doc (MQ algorithm, data format, synthesis, roadmap) - tools/mq_editor/index.html: Web UI (file loader, params, canvas) - tools/mq_editor/fft.js: Radix-2 Cooley-Tukey FFT (from spectral_editor) - tools/mq_editor/mq_extract.js: MQ algorithm (peak detection, tracking, bezier fitting) - tools/mq_editor/viewer.js: Visualization (spectrogram, partials, zoom, axes) - tools/mq_editor/README.md: Usage and implementation status Features: - Load WAV → extract sinusoidal partials → fit cubic bezier curves - Time-frequency spectrogram with hot colormap (0-16 kHz) - Horizontal zoom (mousewheel) around mouse position - Axis ticks with labels (time: seconds, freq: Hz/kHz) - Mouse tooltip showing time/frequency coordinates - Real-time adjustable MQ parameters (FFT size, hop, threshold) Algorithm: - STFT with Hann windows (2048 FFT, 512 hop) - Peak detection with parabolic interpolation - Birth/death/continuation tracking (50 Hz tolerance) - Cubic bezier fitting (4 control points per trajectory) Next: Phase 2 (JS synthesizer for audio preview) handoff(Claude): MQ editor Phase 1 complete. Ready for synthesis implementation.