Fix High-severity real-time audio-thread safety issues from TODO.md
- synthChanged() no longer builds JaySynthActionListener Strings and calls sendActionMessage directly on the audio thread. It now pushes a small POD event into a lock-free SPSC queue (JUCE's AbstractFifo), drained by the existing 10ms UI timer on the message thread, which does the String-building/posting there instead. - VoiceProcessDataV's ~450KB-900KB of SYNTH_MAX_BUFSIZE-sized local arrays are now persistent per-voice buffers allocated once in VoiceSetBufsize (freed in VoiceFree), matching the pattern already used for pBuf_Q and by vcf.c/env.c/lfo.c, instead of being reallocated on the stack on every audio block. - SynthDebug uses vsnprintf instead of vsprintf, bounding writes to its 1024-byte buffer. Verified with clean debug and release builds (no new warnings) and a live playback smoke test in Reaper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011dhtwRLARk4eiPngcQykLJ
This commit is contained in:
@@ -12,11 +12,11 @@ Findings from a code-review pass over `src/plug` and `src/synth` (2026-07-27, br
|
|||||||
- [x] **Unchecked file stream could be null, crashing on plugin construction.** `JaySynth::JaySynth` (`src/plug/JaySynth.cpp:55-57`) now guards the wavetable file read against `createInputStream()` returning null.
|
- [x] **Unchecked file stream could be null, crashing on plugin construction.** `JaySynth::JaySynth` (`src/plug/JaySynth.cpp:55-57`) now guards the wavetable file read against `createInputStream()` returning null.
|
||||||
- [x] **Parameter-changing calls raced with real-time audio rendering — no lock protected them.** `JaySynth::setParameter`/`setControl`/`setPerVoiceControl`/`setParam`/`ClearControls` (`src/plug/JaySynth.cpp`) now take `ScopedLock sl(lock)`, matching `renderNextBlock` and the note/controller handlers.
|
- [x] **Parameter-changing calls raced with real-time audio rendering — no lock protected them.** `JaySynth::setParameter`/`setControl`/`setPerVoiceControl`/`setParam`/`ClearControls` (`src/plug/JaySynth.cpp`) now take `ScopedLock sl(lock)`, matching `renderNextBlock` and the note/controller handlers.
|
||||||
|
|
||||||
## High — real-time audio-thread safety
|
## High — real-time audio-thread safety (fixed)
|
||||||
|
|
||||||
- [ ] **Every note/CC/voice-count change allocates Strings and posts a message from the audio thread.** `JaySynthActionListener::call*` (`src/plug/PluginProcessor.h:41-127`) build messages via chained `String` concatenation, invoked synchronously from `synthChanged` inside `noteOn`/`noteOff`/`handleController`/`Voicestart`/`renderNextBlock` — all on the audio thread whenever the GUI is open. Fix: pass typed structs instead of encoding into Strings, or hop to the message thread before allocating.
|
- [x] **Every note/CC/voice-count change allocated Strings and posted a message from the audio thread.** `synthChanged()` (`src/plug/PluginProcessor.cpp`) no longer calls `JaySynthActionListener::call*` directly from the audio thread. It now pushes a small POD event into a lock-free single-producer/single-consumer queue (JUCE's `AbstractFifo`, added to `PluginProcessor.h`), and the existing 10ms UI `Timer` (`timerCallback`/`dispatchUiEvents`) drains it on the message thread, where the String-building and `sendActionMessage` now safely happen.
|
||||||
- [ ] **~450KB–900KB of stack arrays allocated per voice per audio block.** `VoiceProcessDataV` (`src/synth/voice.c:836-858`) declares ~11 `SYNTH_MAX_BUFSIZE`-sized locals, called once per active voice per block from the worker threads. Fix: hoist these into `voice_common_t`/`voice_t` as pre-allocated buffers (same pattern already used by `vcf.c`/`env.c`/`lfo.c`/`blit.c`).
|
- [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`.
|
||||||
- [ ] **`SynthDebug` uses unbounded `vsprintf` into a fixed 1024-byte buffer plus blocking I/O**, called from `handleMidiEvent` inside the audio-thread render path (`src/synth/synth_debug.c:27-38`). Fix: use `vsnprintf` with the buffer size, and consider a lock-free ring buffer instead of blocking I/O for debug builds.
|
- [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
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ JaySynthAudioProcessorEditor* getEditor(JaySynthAudioProcessor *pThis)
|
|||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JaySynthAudioProcessor::JaySynthAudioProcessor()
|
JaySynthAudioProcessor::JaySynthAudioProcessor()
|
||||||
|
: m_uiEventFifo(uiEventQueueCapacity)
|
||||||
{
|
{
|
||||||
currProgram = 0;
|
currProgram = 0;
|
||||||
currpatch = &patches[currProgram];
|
currpatch = &patches[currProgram];
|
||||||
@@ -57,10 +58,77 @@ JaySynthAudioProcessor::~JaySynthAudioProcessor()
|
|||||||
|
|
||||||
void JaySynthAudioProcessor::timerCallback()
|
void JaySynthAudioProcessor::timerCallback()
|
||||||
{
|
{
|
||||||
|
dispatchUiEvents();
|
||||||
|
|
||||||
if (getEditor(this))
|
if (getEditor(this))
|
||||||
actionListener.callLimiterChanged(m_limiter_active);
|
actionListener.callLimiterChanged(m_limiter_active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JaySynthAudioProcessor::pushUiEvent(int type, int i1, int i2, float f1)
|
||||||
|
{
|
||||||
|
int start1, size1, start2, size2;
|
||||||
|
m_uiEventFifo.prepareToWrite(1, start1, size1, start2, size2);
|
||||||
|
|
||||||
|
if (size1 > 0)
|
||||||
|
{
|
||||||
|
SynthUiEvent &e = m_uiEventBuffer[start1];
|
||||||
|
e.type = type;
|
||||||
|
e.i1 = i1;
|
||||||
|
e.i2 = i2;
|
||||||
|
e.f1 = f1;
|
||||||
|
m_uiEventFifo.finishedWrite(1);
|
||||||
|
}
|
||||||
|
// else: queue is full, drop the event rather than blocking the audio thread.
|
||||||
|
}
|
||||||
|
|
||||||
|
void JaySynthAudioProcessor::dispatchUiEvent(const SynthUiEvent &e)
|
||||||
|
{
|
||||||
|
switch (e.type)
|
||||||
|
{
|
||||||
|
case JaySynth::SYNTH_CHANGED_PARAM:
|
||||||
|
actionListener.callParamChanged(e.i1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JaySynth::SYNTH_CHANGED_MIDICC:
|
||||||
|
actionListener.callMidiControllerChanged(e.i1, e.i2, e.f1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JaySynth::SYNTH_CHANGED_NOTE_PRESSED:
|
||||||
|
actionListener.callMidiNotePressed(e.i1, e.f1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JaySynth::SYNTH_CHANGED_NOTE_RELEASED:
|
||||||
|
actionListener.callMidiNoteReleased(e.i1, e.f1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JaySynth::SYNTH_CHANGED_NUM_VOICES_PLAYING:
|
||||||
|
actionListener.callNumVoicesPlaying(e.i1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JaySynth::SYNTH_CHANGED_MIDICLOCK:
|
||||||
|
actionListener.callMidiClock((uint32_t)e.i1, (uint32_t)e.i2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JaySynthAudioProcessor::dispatchUiEvents(void)
|
||||||
|
{
|
||||||
|
int start1, size1, start2, size2;
|
||||||
|
m_uiEventFifo.prepareToRead(m_uiEventFifo.getNumReady(), start1, size1, start2, size2);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < size1; i++)
|
||||||
|
dispatchUiEvent(m_uiEventBuffer[start1 + i]);
|
||||||
|
|
||||||
|
for (i = 0; i < size2; i++)
|
||||||
|
dispatchUiEvent(m_uiEventBuffer[start2 + i]);
|
||||||
|
|
||||||
|
m_uiEventFifo.finishedRead(size1 + size2);
|
||||||
|
}
|
||||||
|
|
||||||
String JaySynthAudioProcessor::wavesGetPath()
|
String JaySynthAudioProcessor::wavesGetPath()
|
||||||
{
|
{
|
||||||
bool isExistent, isValid;
|
bool isExistent, isValid;
|
||||||
@@ -149,37 +217,37 @@ void JaySynthAudioProcessor::synthChanged(int type, void *pData)
|
|||||||
{
|
{
|
||||||
case JaySynth::SYNTH_CHANGED_PARAM:
|
case JaySynth::SYNTH_CHANGED_PARAM:
|
||||||
if (getEditor(this) != NULL)
|
if (getEditor(this) != NULL)
|
||||||
actionListener.callParamChanged(*((int*)pData));
|
pushUiEvent(type, *((int*)pData), 0, 0.0f);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JaySynth::SYNTH_CHANGED_MIDICC:
|
case JaySynth::SYNTH_CHANGED_MIDICC:
|
||||||
pMidiCC_info = (JaySynth::midiCC_info_t*)pData;
|
pMidiCC_info = (JaySynth::midiCC_info_t*)pData;
|
||||||
applyMidiController(pMidiCC_info, true);
|
applyMidiController(pMidiCC_info, true);
|
||||||
if (getEditor(this) != NULL)
|
if (getEditor(this) != NULL)
|
||||||
actionListener.callMidiControllerChanged(pMidiCC_info->channel, pMidiCC_info->ID, (float)pMidiCC_info->value);
|
pushUiEvent(type, pMidiCC_info->channel, pMidiCC_info->ID, (float)pMidiCC_info->value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JaySynth::SYNTH_CHANGED_NOTE_PRESSED:
|
case JaySynth::SYNTH_CHANGED_NOTE_PRESSED:
|
||||||
pMidi_note_info = (JaySynth::midi_note_info_t*)pData;
|
pMidi_note_info = (JaySynth::midi_note_info_t*)pData;
|
||||||
if (getEditor(this) != NULL)
|
if (getEditor(this) != NULL)
|
||||||
actionListener.callMidiNotePressed(pMidi_note_info->note, (float)pMidi_note_info->velocity);
|
pushUiEvent(type, pMidi_note_info->note, 0, (float)pMidi_note_info->velocity);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JaySynth::SYNTH_CHANGED_NOTE_RELEASED:
|
case JaySynth::SYNTH_CHANGED_NOTE_RELEASED:
|
||||||
pMidi_note_info = (JaySynth::midi_note_info_t*)pData;
|
pMidi_note_info = (JaySynth::midi_note_info_t*)pData;
|
||||||
if (getEditor(this) != NULL)
|
if (getEditor(this) != NULL)
|
||||||
actionListener.callMidiNoteReleased(pMidi_note_info->note, (float)pMidi_note_info->velocity);
|
pushUiEvent(type, pMidi_note_info->note, 0, (float)pMidi_note_info->velocity);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JaySynth::SYNTH_CHANGED_NUM_VOICES_PLAYING:
|
case JaySynth::SYNTH_CHANGED_NUM_VOICES_PLAYING:
|
||||||
if (getEditor(this) != NULL)
|
if (getEditor(this) != NULL)
|
||||||
actionListener.callNumVoicesPlaying(*((int*)pData));
|
pushUiEvent(type, *((int*)pData), 0, 0.0f);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JaySynth::SYNTH_CHANGED_MIDICLOCK:
|
case JaySynth::SYNTH_CHANGED_MIDICLOCK:
|
||||||
pMidi_quarter_info = (JaySynth::midi_quarter_clock_info_t*)pData;
|
pMidi_quarter_info = (JaySynth::midi_quarter_clock_info_t*)pData;
|
||||||
if (getEditor(this) != NULL)
|
if (getEditor(this) != NULL)
|
||||||
actionListener.callMidiClock(pMidi_quarter_info->bpm, pMidi_quarter_info->type);
|
pushUiEvent(type, (int)pMidi_quarter_info->bpm, (int)pMidi_quarter_info->type, 0.0f);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -396,7 +396,29 @@ private:
|
|||||||
int m_limiter_active;
|
int m_limiter_active;
|
||||||
synth_float_t m_limiter_env;
|
synth_float_t m_limiter_env;
|
||||||
JK_MidiClock m_JK_MidiClock;
|
JK_MidiClock m_JK_MidiClock;
|
||||||
|
|
||||||
|
// synthChanged() runs on the audio thread. Building the JaySynthActionListener
|
||||||
|
// Strings and posting them there (as JAYSYNTH_CHANGED_* used to do directly)
|
||||||
|
// allocates and locks on the audio thread. Instead, synthChanged() pushes a
|
||||||
|
// small POD event into this lock-free single-producer (audio thread) /
|
||||||
|
// single-consumer (message-thread timer) queue, and timerCallback() drains it
|
||||||
|
// and does the actual String-building/sendActionMessage on the message thread.
|
||||||
|
struct SynthUiEvent
|
||||||
|
{
|
||||||
|
int type;
|
||||||
|
int i1;
|
||||||
|
int i2;
|
||||||
|
float f1;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum { uiEventQueueCapacity = 256 };
|
||||||
|
SynthUiEvent m_uiEventBuffer[uiEventQueueCapacity];
|
||||||
|
AbstractFifo m_uiEventFifo;
|
||||||
|
|
||||||
|
void pushUiEvent(int type, int i1, int i2, float f1);
|
||||||
|
void dispatchUiEvent(const SynthUiEvent &e);
|
||||||
|
void dispatchUiEvents(void);
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JaySynthAudioProcessor);
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JaySynthAudioProcessor);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ void SynthDebug(const char *fmtstr, ...)
|
|||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
va_start(args, fmtstr);
|
va_start(args, fmtstr);
|
||||||
vsprintf(buf, fmtstr, args);
|
vsnprintf(buf, sizeof(buf), fmtstr, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
OutputDebugString(buf);
|
OutputDebugString(buf);
|
||||||
|
|||||||
+90
-12
@@ -121,6 +121,21 @@ void VoiceInit(voice_t *pObj, voice_common_t *pCom, UINT32 id, synth_float_t fs)
|
|||||||
|
|
||||||
pObj->pBuf_Q = NULL;
|
pObj->pBuf_Q = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < (INT32)VOICE_NUM_OSC; i++)
|
||||||
|
{
|
||||||
|
pObj->pBuf_VCO_fmout[i] = NULL;
|
||||||
|
pObj->pBuf_vco_pitch[i] = NULL;
|
||||||
|
pObj->pBuf_osc_out_smoothed[i] = NULL;
|
||||||
|
}
|
||||||
|
pObj->pBuf_vco_pwm = NULL;
|
||||||
|
pObj->pBuf_vco_fm = NULL;
|
||||||
|
pObj->pBuf_vco_am = NULL;
|
||||||
|
pObj->pBuf_vcf_fm = NULL;
|
||||||
|
pObj->pBuf_vcf_qm = NULL;
|
||||||
|
pObj->pBuf_vca_am = NULL;
|
||||||
|
pObj->pBuf_vca_pan = NULL;
|
||||||
|
pObj->pBuf_osc_out = NULL;
|
||||||
|
|
||||||
VoiceSetBufsize(pObj, SYNTH_MAX_BUFSIZE);
|
VoiceSetBufsize(pObj, SYNTH_MAX_BUFSIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,11 +264,65 @@ void VoiceSetBufsize(voice_t *pObj, UINT32 size)
|
|||||||
|
|
||||||
pObj->pBuf_Q = NULL;
|
pObj->pBuf_Q = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < (INT32)VOICE_NUM_OSC; i++)
|
||||||
|
{
|
||||||
|
if (pObj->pBuf_VCO_fmout[i])
|
||||||
|
free(pObj->pBuf_VCO_fmout[i]);
|
||||||
|
pObj->pBuf_VCO_fmout[i] = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_vco_pitch[i])
|
||||||
|
free(pObj->pBuf_vco_pitch[i]);
|
||||||
|
pObj->pBuf_vco_pitch[i] = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_osc_out_smoothed[i])
|
||||||
|
free(pObj->pBuf_osc_out_smoothed[i]);
|
||||||
|
pObj->pBuf_osc_out_smoothed[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pObj->pBuf_vco_pwm) free(pObj->pBuf_vco_pwm);
|
||||||
|
pObj->pBuf_vco_pwm = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_vco_fm) free(pObj->pBuf_vco_fm);
|
||||||
|
pObj->pBuf_vco_fm = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_vco_am) free(pObj->pBuf_vco_am);
|
||||||
|
pObj->pBuf_vco_am = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_vcf_fm) free(pObj->pBuf_vcf_fm);
|
||||||
|
pObj->pBuf_vcf_fm = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_vcf_qm) free(pObj->pBuf_vcf_qm);
|
||||||
|
pObj->pBuf_vcf_qm = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_vca_am) free(pObj->pBuf_vca_am);
|
||||||
|
pObj->pBuf_vca_am = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_vca_pan) free(pObj->pBuf_vca_pan);
|
||||||
|
pObj->pBuf_vca_pan = NULL;
|
||||||
|
|
||||||
|
if (pObj->pBuf_osc_out) free(pObj->pBuf_osc_out);
|
||||||
|
pObj->pBuf_osc_out = NULL;
|
||||||
|
|
||||||
if (!size)
|
if (!size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pObj->pBuf_Q = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
pObj->pBuf_Q = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
|
||||||
|
for (i = 0; i < (INT32)VOICE_NUM_OSC; i++)
|
||||||
|
{
|
||||||
|
pObj->pBuf_VCO_fmout[i] = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_vco_pitch[i] = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_osc_out_smoothed[i] = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
pObj->pBuf_vco_pwm = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_vco_fm = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_vco_am = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_vcf_fm = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_vcf_qm = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_vca_am = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_vca_pan = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
|
pObj->pBuf_osc_out = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoiceEvent(voice_t *pObj, UINT32 type)
|
void VoiceEvent(voice_t *pObj, UINT32 type)
|
||||||
@@ -844,18 +913,27 @@ void VoiceProcessDataV(voice_t *pObj, float *pOut1, float *pOut2, UINT32 len)
|
|||||||
synth_float_t *pModSrc[VOICE_NUM_MOD_SOURCES];
|
synth_float_t *pModSrc[VOICE_NUM_MOD_SOURCES];
|
||||||
voice_common_t *pCom = pObj->pCom;
|
voice_common_t *pCom = pObj->pCom;
|
||||||
|
|
||||||
// Temp Variables
|
// Temp Variables - these point at persistent per-voice buffers (allocated
|
||||||
synth_float_t VCO_fmout[VOICE_NUM_OSC][SYNTH_MAX_BUFSIZE];
|
// in VoiceSetBufsize) rather than being call-local arrays, to avoid
|
||||||
synth_float_t vco_pwm[SYNTH_MAX_BUFSIZE];
|
// ~450KB-900KB of stack allocation on every audio block.
|
||||||
synth_float_t vco_fm[SYNTH_MAX_BUFSIZE];
|
synth_float_t *VCO_fmout[VOICE_NUM_OSC];
|
||||||
synth_float_t vco_am[SYNTH_MAX_BUFSIZE];
|
synth_float_t *vco_pwm = pObj->pBuf_vco_pwm;
|
||||||
synth_float_t vcf_fm[SYNTH_MAX_BUFSIZE];
|
synth_float_t *vco_fm = pObj->pBuf_vco_fm;
|
||||||
synth_float_t vcf_qm[SYNTH_MAX_BUFSIZE];
|
synth_float_t *vco_am = pObj->pBuf_vco_am;
|
||||||
synth_float_t vca_am[SYNTH_MAX_BUFSIZE];
|
synth_float_t *vcf_fm = pObj->pBuf_vcf_fm;
|
||||||
synth_float_t vca_pan[SYNTH_MAX_BUFSIZE];
|
synth_float_t *vcf_qm = pObj->pBuf_vcf_qm;
|
||||||
synth_float_t vco_pitch[VOICE_NUM_OSC][SYNTH_MAX_BUFSIZE];
|
synth_float_t *vca_am = pObj->pBuf_vca_am;
|
||||||
synth_float_t osc_out[SYNTH_MAX_BUFSIZE];
|
synth_float_t *vca_pan = pObj->pBuf_vca_pan;
|
||||||
synth_float_t osc_out_smoothed[VOICE_NUM_OSC][SYNTH_MAX_BUFSIZE];
|
synth_float_t *vco_pitch[VOICE_NUM_OSC];
|
||||||
|
synth_float_t *osc_out = pObj->pBuf_osc_out;
|
||||||
|
synth_float_t *osc_out_smoothed[VOICE_NUM_OSC];
|
||||||
|
|
||||||
|
for (i = 0; i < VOICE_NUM_OSC; i++)
|
||||||
|
{
|
||||||
|
VCO_fmout[i] = pObj->pBuf_VCO_fmout[i];
|
||||||
|
vco_pitch[i] = pObj->pBuf_vco_pitch[i];
|
||||||
|
osc_out_smoothed[i] = pObj->pBuf_osc_out_smoothed[i];
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Process parameter smoothing
|
// Process parameter smoothing
|
||||||
|
|||||||
@@ -305,6 +305,20 @@ typedef struct _svoice_t
|
|||||||
smooth_t smooth_vco_enable[VOICE_NUM_OSC];
|
smooth_t smooth_vco_enable[VOICE_NUM_OSC];
|
||||||
smooth_t smooth_vco_portamento[VOICE_NUM_OSC];
|
smooth_t smooth_vco_portamento[VOICE_NUM_OSC];
|
||||||
synth_float_t vco_pitch[VOICE_NUM_OSC];
|
synth_float_t vco_pitch[VOICE_NUM_OSC];
|
||||||
|
// Scratch buffers used only within VoiceProcessDataV (voice.c), allocated
|
||||||
|
// once per voice (see VoiceSetBufsize) instead of as call-local arrays, to
|
||||||
|
// avoid ~450KB-900KB of stack allocation on every audio block.
|
||||||
|
synth_float_t *pBuf_VCO_fmout[VOICE_NUM_OSC];
|
||||||
|
synth_float_t *pBuf_vco_pwm;
|
||||||
|
synth_float_t *pBuf_vco_fm;
|
||||||
|
synth_float_t *pBuf_vco_am;
|
||||||
|
synth_float_t *pBuf_vcf_fm;
|
||||||
|
synth_float_t *pBuf_vcf_qm;
|
||||||
|
synth_float_t *pBuf_vca_am;
|
||||||
|
synth_float_t *pBuf_vca_pan;
|
||||||
|
synth_float_t *pBuf_vco_pitch[VOICE_NUM_OSC];
|
||||||
|
synth_float_t *pBuf_osc_out;
|
||||||
|
synth_float_t *pBuf_osc_out_smoothed[VOICE_NUM_OSC];
|
||||||
// smooth_t *pParam_smoother[VOICE_NUM_PARAMS];
|
// smooth_t *pParam_smoother[VOICE_NUM_PARAMS];
|
||||||
// synth_float_t param_smoothed[VOICE_NUM_PARAMS][SYNTH_MAX_BUFSIZE];
|
// synth_float_t param_smoothed[VOICE_NUM_PARAMS][SYNTH_MAX_BUFSIZE];
|
||||||
} voice_t;
|
} voice_t;
|
||||||
|
|||||||
Reference in New Issue
Block a user