Fix Memory-management issues from TODO.md
- JaySynth's destructor now uses delete[] to match every new T[n] allocation (m_pVoices, pPer_voice_controls, pCurrNoteInfos, humanize_voice_param[i], ppHumanizedSliders[i], m_ppAudioThread, m_ppEventAudioThreadRdy), fixing undefined behavior from the mismatched scalar delete. - Found and fixed the same new[]/delete mismatch pattern via ScopedPointer in PluginProcessor.cpp: ScopedPointer always calls scalar delete (per JUCE's own doc comment "do not give it an array to hold!"), so ScopedPointer<char> holding a new char[...] in setStateInformation/setCurrentProgramStateInformation had the same bug. Replaced both with HeapBlock<char>, JUCE's array-owning, malloc/free-backed smart pointer. - Added a shared SynthCheckAlloc() helper (synth_defs.h/synth_debug.c) that aborts with a diagnostic instead of returning NULL, and wrapped all 24 malloc call sites across the C DSP core (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-thread overhead. Verified with clean debug and release builds (no new warnings/errors) and a full link. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011dhtwRLARk4eiPngcQykLJ
This commit is contained in:
@@ -18,10 +18,10 @@ Findings from a code-review pass over `src/plug` and `src/synth` (2026-07-27, br
|
||||
- [x] **~450KB–900KB of stack arrays allocated per voice per audio block.** `VoiceProcessDataV`'s 11 `SYNTH_MAX_BUFSIZE`-sized locals (`src/synth/voice.c`) are now persistent per-voice buffers (`pBuf_VCO_fmout`, `pBuf_vco_pwm`, etc., declared in `voice.h`), allocated once in `VoiceSetBufsize`/freed in `VoiceFree` — same lifecycle/pattern already used by `pBuf_Q` and by `vcf.c`/`env.c`/`lfo.c`.
|
||||
- [x] **`SynthDebug` used unbounded `vsprintf` into a fixed 1024-byte buffer.** `src/synth/synth_debug.c` now uses `vsnprintf` with the buffer size. (Left the blocking `puts()`/`OutputDebugString` I/O as-is — it's compiled out entirely unless `SYNTH_DEBUG` is defined, so release builds are unaffected, and a lock-free logging redesign felt like overreach for a debug-only path.)
|
||||
|
||||
## Memory management
|
||||
## Memory management (fixed)
|
||||
|
||||
- [ ] **`new[]`/`delete` mismatches (UB) in `JaySynth`'s destructor** — `m_pVoices`, `pPer_voice_controls`, `pCurrNoteInfos`, `humanize_voice_param[i]`, `ppHumanizedSliders[i]`, `m_ppAudioThread`, `m_ppEventAudioThreadRdy` all allocated with `new T[n]` but freed with scalar `delete` (`src/plug/JaySynth.cpp:62/186, 77/184, 80/185, 100-101/173-174, 124-125/160,191`). Fix: change to `delete[]` at each site.
|
||||
- [ ] **`malloc` is never null-checked** across the entire C DSP core (`env.c`, `lfo.c`, `vcf.c`, `vco.c`, `blit.c`, `wavetable.c`, `voice.c`, `param_scale.c`). Fix: check and fail gracefully (or `abort()` with a diagnostic) rather than crashing on first audio callback.
|
||||
- [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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user