Files
JaySynth/src/synth/vco.c
T
jensandClaude Sonnet 5 54a67562db 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
2026-07-27 18:19:56 +02:00

205 lines
4.8 KiB
C

// --------------------------------------------------------------
// --------------------------------------------------------------
#include <string.h>
#include <stdlib.h>
#include "synth_defs.h"
#include "blit.h"
#include "vco.h"
#include "wavetable.h"
// --------------------------------------------------------------
// internal funcs
// --------------------------------------------------------------
// --------------------------------------------------------------
// Exported functions
// --------------------------------------------------------------
void VCO_ModInit(vco_common_t *pCom)
{
BLIT_ModInit(&pCom->blit);
}
void VCO_ModFree(vco_common_t *pCom)
{
BLIT_ModFree(&pCom->blit);
}
void VCO_Init(osc_t *pObj, UINT32 id, vco_common_t *pCom, synth_float_t fs)
{
pObj->pCom = pCom;
pObj->id = id;
pObj->fs = fs;
if (pObj->id == 0)
{
VCO_ModInit(pCom);
}
pObj->pOut = NULL;
pObj->bufsize = 0;
VCO_SetBufsize(pObj, SYNTH_MAX_BUFSIZE);
pObj->param[OSC_PARAM2_WAVEFORM] = OSC_WAVEFORM_SAWTOOTH;
Sine_Init(&pObj->sine, fs);
BLIT_Init(&pObj->blep, &pCom->blit, fs);
WT_Init(&pObj->wt, id, &pCom->wt, fs);
pObj->impulse = OSC_IMPULS_HEIGHT;
}
void VCO_Free(osc_t *pObj)
{
Sine_Free(&pObj->sine);
BLIT_Free(&pObj->blep);
WT_Free(&pObj->wt);
VCO_SetBufsize(pObj, 0);
if (pObj->id == 0)
{
VCO_ModFree(pObj->pCom);
}
}
void VCO_SetFS(osc_t *pObj, synth_float_t fs)
{
pObj->fs = fs;
Sine_SetFS(&pObj->sine, fs);
BLIT_SetFS(&pObj->blep, fs);
WT_SetFS(&pObj->wt, fs);
}
void VCO_SetBufsize(osc_t *pObj, UINT32 size)
{
BLIT_SetBufsize(&pObj->blep, size);
if (pObj->bufsize == size)
return;
pObj->bufsize = size;
if (pObj->pOut)
free(pObj->pOut);
pObj->pOut = NULL;
if (!size)
return;
pObj->pOut = (synth_float_t*)SynthCheckAlloc(malloc(pObj->bufsize*sizeof(synth_float_t)));
}
void VCO_Reset(osc_t *pObj, synth_float_t phase)
{
Sine_Reset(&pObj->sine, phase);
BLIT_Reset(&pObj->blep, phase);
WT_Reset(&pObj->wt, phase);
pObj->impulse = OSC_IMPULS_HEIGHT;
}
void VCO_Start(osc_t *pObj)
{
Sine_Start(&pObj->sine);
BLIT_Start(&pObj->blep);
WT_Start(&pObj->wt);
}
void VCO_Param2Set(osc_t *pObj, UINT32 type, synth_float_t value)
{
UINT32 wt_table;
switch(type)
{
case OSC_PARAM2_WAVEFORM:
if (pObj->param[type] == (UINT32)value)
break;
pObj->param[type] = (UINT32)value;
if ((pObj->param[OSC_PARAM2_WAVEFORM] >= OSC_WAVEFORM_WAVETABLE) && (pObj->param[OSC_PARAM2_WAVEFORM] < (OSC_WAVEFORM_WAVETABLE+WT_NUM_WAVETABLES)))
{
wt_table = (UINT32)(pObj->param[OSC_PARAM2_WAVEFORM]-OSC_WAVEFORM_WAVETABLE);
// WT_Param2Set(&pObj->wt, WT_PARAM2_WAVEFORM, WT_WAVEFORM_REGULAR);
WT_Param2Set(&pObj->wt, WT_PARAM2_WAVEFORM, WT_WAVEFORM_INTERPOLATED);
WT_Param2Set(&pObj->wt, WT_PARAM2_WAVETABLE_ID, wt_table);
}
Sine_Prepare(&pObj->sine);
BLIT_Prepare(&pObj->blep, 1024);
WT_Prepare(&pObj->wt);
break;
case OSC_PARAM2_DUTYCYCLE:
pObj->param[type] = value;
BLIT_SetDutyCycle(&pObj->blep, value);
WT_Param2Set(&pObj->wt, WT_PARAM2_WAVETABLE_ENTRY, value);
break;
default:
break;
}
}
void VCO_ProcessDataV(osc_t *pObj, synth_float_t *pPitch, synth_float_t *pCV_fm, synth_float_t *pCV_pwm, UINT32 *pSyncIn, UINT32 *pSyncOut, UINT32 len)
{
UINT32 i, is_slave;
synth_float_t *pOut;
synth_float_t out;
pOut = pObj->pOut;
is_slave = (pSyncIn != NULL) && (pSyncOut == NULL);
if (pSyncOut)
memset(pSyncOut, 0, len*sizeof(synth_float_t));
// Create output
if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_SAWTOOTH)
{
BLIT_Process_SAW_Vector(&pObj->blep, pPitch, pCV_fm, pSyncIn, pSyncOut, pObj->pOut, len);
}
if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_SQUARE)
{
BLIT_Process_SQR_Vector(&pObj->blep, pPitch, pCV_fm, pCV_pwm, pSyncIn, pSyncOut, pObj->pOut, len);
}
if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_TRIANGLE)
{
BLIT_Process_TRI_Vector(&pObj->blep, pPitch, pCV_fm, pSyncIn, pSyncOut, pObj->pOut, len);
}
if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_SINE)
{
for (i=0; i< len; i++)
{
out = Sine_Process_Scalar(&pObj->sine, pPitch[i], pCV_fm[i]);
if(is_slave && pSyncIn[i])
{
Sine_Reset(&pObj->sine, 0);
}
*(pOut++) = out;
}
}
if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_IMPULSE)
{
for (i=0; i< len; i++)
{
out = pObj->impulse;
pObj->impulse = 0;
*(pOut++) = out;
}
}
if ((pObj->param[OSC_PARAM2_WAVEFORM] >= OSC_WAVEFORM_WAVETABLE) && (pObj->param[OSC_PARAM2_WAVEFORM] < (OSC_WAVEFORM_WAVETABLE+WT_NUM_WAVETABLES)))
{
WT_ProcessDataV(&pObj->wt, pPitch, pCV_fm, pCV_pwm, pObj->pOut, len);
}
}
synth_float_t* VCO_GetProcessBuffer(osc_t *pObj)
{
return pObj->pOut;
}