Fix Patch/Bank import-export issues from TODO.md
- patchImportXml: handle the legacy single-patch format (JSYNTH_PATCH root) as its own top-level branch instead of incorrectly nested inside the modern per-child loop, which checked/decoded the outer xml on every iteration instead of the loop variable xml2. Switched its CC-table import to import_midi_cc, matching the convention already used (and exercised) by bankDecodeXml_legacy. loadPatchFromFile's .xmp branch now also accepts legacy-rooted files. - setCurrentProgramStateInformation: fix a related bug found in passing - it was passing patchImportXml an XML node one level too deep, making the VST2 "copy plugin state to another track" feature a complete no-op. - Fix the fundamentally broken fxb/fxp magic-number check: it compared raw file bytes (as text, e.g. "CcnK") against JUCE's int-to-decimal- text String constructor (e.g. "1130589771"), which can never match. This meant the entire opaque .fxb/.fxp load path was dead code. Fixed using ByteOrder::bigEndianInt, matching the pattern JUCE's own VST3 wrapper uses for the identical struct. "Regular" (non-chunk) .fxb/.fxp is still unsupported, but now shows an AlertWindow explaining why instead of silently doing nothing. - Implement .fxp/.fxb saving using the same opaque-chunk format the load path expects, reusing getCurrentProgramStateInformation/ getStateInformation for the payload. - Deduplicate patchDecodeXml/patchDecodeXml_legacy (the latter now just delegates to the former). Verified with clean debug/release builds and a standalone round-trip test of the binary format logic (magic detection, byte order, size/payload round-trip, safe rejection of truncated/bogus files). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011dhtwRLARk4eiPngcQykLJ
This commit is contained in:
@@ -23,12 +23,12 @@ Findings from a code-review pass over `src/plug` and `src/synth` (2026-07-27, br
|
||||
- [x] **`new[]`/`delete` mismatches (UB) in `JaySynth`'s destructor** — `m_pVoices`, `pPer_voice_controls`, `pCurrNoteInfos`, `humanize_voice_param[i]`, `ppHumanizedSliders[i]`, `m_ppAudioThread`, `m_ppEventAudioThreadRdy` (`src/plug/JaySynth.cpp`) all now freed with `delete[]` to match their `new T[n]` allocations. Also found and fixed the same pattern via `ScopedPointer` in `PluginProcessor.cpp` (`setStateInformation`/`setCurrentProgramStateInformation`) — `ScopedPointer` always calls scalar `delete` (per JUCE's own doc comment), so `ScopedPointer<char> pUncompressedData = new char[...]` was the same bug; replaced with `HeapBlock<char>`, JUCE's array-owning smart pointer.
|
||||
- [x] **`malloc` was never null-checked** across the C DSP core. Added a shared `SynthCheckAlloc()` helper (`synth_defs.h`/`synth_debug.c`) that aborts with a clear diagnostic instead of returning NULL, and wrapped all 24 `malloc` call sites across `env.c`, `lfo.c`, `vcf.c`, `vco.c`, `blit.c`, `wavetable.c`, `voice.c`, `param_scale.c`. All are one-time init/bufsize-change calls, never in the per-block render path, so this adds no real-time overhead.
|
||||
|
||||
## Patch/Bank import-export
|
||||
## Patch/Bank import-export (fixed)
|
||||
|
||||
- [ ] **Legacy patch import decodes the wrong XML node** — uses outer `xml` instead of loop variable `xml2` in the legacy branch (`src/plug/PluginProcessor.cpp:959-991`), unlike every other branch; currently masked but re-decodes redundantly and is a latent bug if the legacy format gains child elements.
|
||||
- [ ] **"Regular" (non-chunk) `.fxb`/`.fxp` files are silently accepted but never loaded** — `is_regular` is computed and never used (`src/plug/PluginProcessor.cpp:834-852`, `930-948`). Fix: either implement the regular-format path or surface an error to the user.
|
||||
- [ ] **`.fxp`/`.fxb` saving is entirely unimplemented** (`savePatchToFile`/`saveBankToFile`, `src/plug/PluginProcessor.cpp:1027-1067`, both marked `// ToDo`) while loading is implemented — asymmetric format support.
|
||||
- [ ] **`patchDecodeXml` and `patchDecodeXml_legacy` are near-duplicated**, including a copy-pasted legacy frequency→cents conversion special case (`src/plug/PluginProcessor.cpp:749-765`, `767-783`). Fix: factor out the shared per-parameter decode loop.
|
||||
- [x] **Legacy patch import decoded the wrong XML node.** `patchImportXml` now handles the legacy single-patch format (`JSYNTH_PATCH` root) as its own top-level branch instead of incorrectly nested inside the modern per-child loop, which checked/decoded the outer `xml` on every iteration instead of the loop variable. Also switched its CC-table import to `import_midi_cc` (matching the actually-exercised convention already used by `bankDecodeXml_legacy`, since this branch was previously unreachable dead code). `loadPatchFromFile`'s `.xmp` branch now also accepts legacy-rooted files, so single legacy patches can actually be loaded. Also fixed a related bug found in passing: `setCurrentProgramStateInformation` (VST2 "copy plugin state to another track") was passing `patchImportXml` an XML node one level too deep, making it a complete no-op.
|
||||
- [x] **Found a much bigger bug while investigating "regular .fxb/.fxp silently ignored": the magic-number check itself was completely broken.** It compared `String((const char*)&bank->chunkMagic)` (raw file bytes as text, e.g. `"CcnK"`) against `String(cMagic)` (JUCE's `int`-to-*decimal-text* constructor, e.g. `"1130589771"`) — these can never be equal, so **the entire opaque `.fxb`/`.fxp` load path was dead code**, regardless of format. Fixed using `ByteOrder::bigEndianInt`, the correct byte-order-aware comparison (matches the pattern JUCE's own VST3 wrapper uses for the identical struct). "Regular" (non-chunk) format is still not supported for load, but now surfaces an `AlertWindow` explaining why instead of silently doing nothing. Verified with a standalone round-trip test of the binary format logic (magic detection, size/payload round-trip, safe rejection of truncated/bogus files).
|
||||
- [x] **`.fxp`/`.fxb` saving was unimplemented.** Now implemented using the same opaque-chunk format the (now-fixed) load path expects, reusing `getCurrentProgramStateInformation`/`getStateInformation` for the payload.
|
||||
- [x] **`patchDecodeXml`/`patchDecodeXml_legacy` duplication** — `_legacy` now just delegates to `patchDecodeXml`.
|
||||
|
||||
## Design
|
||||
|
||||
|
||||
Reference in New Issue
Block a user