| Age | Commit message (Collapse) | Author |
|
- seq_compiler: add gbuf_albedo/gbuf_rgba32uint to NODE_TYPES
- timeline: declare gbuf_feat0/feat1 as gbuf_rgba32uint, route
CNNv3Effect output through cnn_v3_out (gbuf_albedo) + Passthrough
to sink (dec0 can't write directly to Rgba8Unorm sink)
- cnn_v3_effect: fix update_bind_groups using .set() instead of
.replace() causing FATAL assert on second frame
- TODO: add CNN v3 "2D mode" (G-buffer-free) future task
handoff(Gemini): CNNv3Effect now runs without crashes at --seek 48
|
|
path
|
|
- CNNv3Effect constructor loads ASSET_WEIGHTS_CNN_V3 via GetAsset on startup
- seq_compiler.py: CLASS_TO_HEADER supports full #include paths for cnn_v3/ classes
- timeline.seq: add cnn_v3_test sequence at 48s (GBufferEffect → CNNv3Effect)
- test_cnn_v3_parity: zero_weights test now explicitly uploads zeros to override asset
handoff(Gemini): CNNv3Effect ready; export weights to workspaces/main/weights/ and seek to 48s to test
|
|
|
|
asset_packer now emits a zero-size empty stub for BINARY assets whose
file is not found, and continues with a warning rather than aborting.
Allows building without optional assets like cnn_v2_weights.bin.
handoff(Gemini): asset_packer tolerates missing BINARY files; GetAsset()
returns nullptr/size=0 for those assets at runtime.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Wine/Vulkan returns WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal
instead of SuccessOptimal, causing the blit pass to be skipped entirely
and the window to stay black.
Fixed in seq_compiler.py (the source template); regenerated timeline.cc.
Tests: 35/35.
handoff(Gemini): Wine black screen fixed — root cause was status code
check rejecting suboptimal swapchain; exe now renders on Wine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add CLASS_TO_HEADER override map for classes that share a header file.
NtscYiq lives in ntsc_effect.h alongside Ntsc.
handoff(Gemini): seq_compiler.py fix for shared-header effect classes.
|
|
- asset_packer: include WGSL in --disk_load path storage (alongside SPEC/MP3)
- asset_manager: disk-load WGSL assets at runtime when !DEMO_STRIP_ALL
- DemoCodegen: pass ASSET_PACKER_FLAGS to pack_test_assets so test assets
also use disk-load paths in dev mode (fixes pre-existing SPEC/WGSL test failures)
- test_shader_composer: fix stale assertions (fn test_wgsl → fn snippet_a,
correct ordering check)
35/35 tests passing.
handoff(Claude): WGSL disk-loading implemented. Shaders now loaded from disk
in dev mode, enabling hot-reload without rebuild. Tests fixed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
This commit introduces a dual-mode asset loading system to enhance developer workflow and optimize release builds.
Key changes include:
- **Dual-Mode Asset Loading:** Assets are now loaded from disk during development (when is OFF) for faster iteration, particularly for heavy assets like and files. For release builds ( ON), all assets are embedded directly into the binary, ensuring a single, self-contained executable.
- **Explicit Asset Typing:** Replaced generic asset type with specific types (, , , , ) in and the enum in for clearer categorization and more robust processing.
- **Build System Integration:** Modified to pass a flag to in development builds.
- **Asset Packer Updates:** now generates file paths for disk-loaded assets and embeds data for others based on the build mode.
- **Asset Manager Enhancements:** includes new logic in for loading and caching disk-based assets and updates to for proper memory deallocation.
- **Documentation:** Updated to reflect the new asset nomenclature and dual-mode loading strategy.
- **Project Rules:** Added a concise rule to mandating top-level documentation updates for medium/large sub-system changes.
handoff(Gemini): Implemented dual-mode asset loading and updated documentation.
|
|
|
|
- Remove erroneous Hann synthesis window from synth.cc (g_hann * tmp[j]).
Hann analysis at 50% overlap satisfies w[n]+w[n+H]=1, so rectangular
synthesis gives perfect reconstruction; applying Hann twice was wrong.
- Extract ola_encode()/ola_decode()/ola_num_frames() into src/audio/ola.h+cc.
spectool and test_wav_roundtrip now use the shared functions.
synth.cc lazy-decode path stays inlined (see TODO for future refactor).
- Drop dead <atomic> include and g_hann array from synth.cc.
- Drop dead window.h include from spectool.cc.
- Update PROJECT_CONTEXT.md, COMPLETED.md, TODO.md to reflect correct
analysis-only Hann window and new ola.h API.
handoff(Gemini): OLA synthesis bug fixed + ola.h factorized. synth.cc
lazy-decode still inline (TODO item added). 34/35 tests pass; WavDumpBackendTest
failure is pre-existing and unrelated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
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>
|
|
- 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>
|
|
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>
|
|
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.
|
|
|
|
handoff(Gemini): tracker_compiler now handles single-line and multi-line block comments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
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.
|
|
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>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- 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>
|
|
- 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>
|
|
- 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>
|
|
- 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>
|