- 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
205 lines
4.8 KiB
C
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;
|
|
}
|
|
|