summaryrefslogtreecommitdiff
path: root/tools
AgeCommit message (Collapse)Author
2026-03-05chore: remove dead specplay tool and all referencesskal
specplay was removed from the build but source/docs remained. Delete tools/specplay.cc, tools/specplay_README.md, and remove specplay sections from TOOLS_REFERENCE.md and BACKLOG.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05feat(spectool): add --wav decode, IMDCT, and roundtrip testskal
- spectool --wav <input.spec> <output.wav>: decodes .spec to mono 16-bit WAV at 32 kHz using IDCT-OLA synthesis (no synthesis window). The analysis Hann window at 50% overlap satisfies w[n]+w[n+H]=1, so the synthesis window must be rectangular for perfect reconstruction. - Add imdct_512 / imdct_fft to audio lib (fft.cc, fft.h, idct.cc, dct.h) for future MDCT-based synthesis. - test_wav_roundtrip: in-process OLA analyze+decode SNR test (≥30 dB). Currently measures 53 dB on a 440 Hz sine. - Fix stale test_spectool.cc: version assertion updated from 1 to SPEC_VERSION_V2_OLA (was always wrong since OLA fix landed). - Docs: TOOLS_REFERENCE.md removes dead specview, documents --wav / --normalize / test_gen. HOWTO.md adds decode section. TRACKER.md notes spec v2 OLA format and decode command. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05fix(audio): OLA encoder never ran; version never propagated to decoderskal
Two bugs kept the v2 OLA path permanently disabled: 1. SpectrogramResourceManager::load_asset() never set spec.version from SpecHeader::version — all .spec assets loaded with version=0, so ola_mode was always false in the voice. 2. spectool analyze_audio() used non-overlapping chunks (stride=DCT_SIZE), hamming_window_512, and hardcoded header.version=1 — OLA analysis was never implemented in the encoder. Fixes: propagate header->version in load_asset(); switch spectool to OLA_HOP_SIZE stride, hann_window_512, and SPEC_VERSION_V2_OLA. Regenerated all .spec files. handoff(Gemini): OLA enc/dec chain now correct end-to-end. .spec files are v2 (50% overlap, Hann). No API changes; 33/34 tests pass (WavDumpBackendTest pre-existing failure unrelated). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05fix(spectool): pre-fill audio buffer before audio_start() in play modeskal
Trigger voice and render ahead before starting the audio device to avoid "Audio buffer not pre-filled" warning and silent playback. handoff(Claude): spectool play now produces sound correctly.
2026-03-05remove dead tool 'specview'skal
2026-03-05feat(tracker): add /* */ block comment support to .track parserskal
handoff(Gemini): tracker_compiler now handles single-line and multi-line block comments. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03feat(assets): replace is_procedural/is_gpu_procedural bools with AssetType ↵skal
enum, add MP3 type - Add AssetType enum {STATIC, PROC, PROC_GPU, MP3} to AssetRecord - Add GetAssetType() API to asset_manager.h/cc - asset_packer: parse 'MP3' compression keyword in assets.txt - tracker: remove magic-byte is_mp3_asset(); use GetAssetType() instead - assets.txt: NEVER_MP3 now uses 'MP3' compression type - doc/ASSET_SYSTEM.md: rewritten to document new types and API handoff(Gemini): AssetType enum landed; MP3 detection is now explicit via asset metadata.
2026-02-28fix(shadertoy): update convert_shadertoy.py shader output path to src/effects/skal
Shader output and Next Steps instructions now use src/effects/<name>.wgsl instead of workspaces/main/shaders/<name>.wgsl. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28feat(tools/shadertoy): add --dry-run flag to skip file writingskal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28fix(tools/shadertoy): fix Next Steps instructions and update EFFECT_WORKFLOW.mdskal
- Fix assets.txt path: shaders/xxx.wgsl (not ../../src/shaders/) - Fix shader output path to workspaces/main/shaders/ - Step 5: reference cmake/DemoSourceLists.cmake COMMON_GPU_EFFECTS (not CMakeLists.txt GPU_SOURCES) - Add step for test_demo_effects.cc Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28fix(tools/shadertoy): sync templates and script to current codebase conventionsskal
- template.h/cc: new Effect constructor/render signatures, RAII wrappers, HEADLESS_RETURN_IF_NULL, #pragma once - template.wgsl: sequence_uniforms + render/fullscreen_uv_vs includes, UniformsSequenceParams at binding 2, VertexOutput in fs_main - convert_shadertoy.py: paths src/effects/ + src/shaders/, new Effect pattern (create_post_process_pipeline, pp_update_bind_group), correct field names (beat_time/beat_phase), updated next-steps instructions - README.md: streamlined to quick-ref; accurate GLSL→WGSL table and uniforms Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28fix(shader_editor): sync CommonUniforms to current struct, fix layoutskal
- Update CommonUniforms: remove _pad0/_pad1, add beat_time/beat_phase, move _pad to end (matches src/shaders/common_uniforms.wgsl) - Fix JS uniform write order to match new layout - Fix flex-direction: row override (common.css forced column, hiding editor) 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>