T12: native audio loop SPSC + WASAPI (exclusive/shared, zero-lock audio thread, SendNote -> SPSC)
This commit is contained in:
@@ -23,6 +23,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include "vestige.h"
|
||||
#include "AudioEngine.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -45,6 +48,8 @@ struct Instance
|
||||
int32 handle = 0;
|
||||
SF_ParamChangedCallback paramCb = nullptr;
|
||||
void* paramCbUserdata = nullptr;
|
||||
sonicforge::AudioEngine audio; // T12: native audio loop (SPSC + WASAPI)
|
||||
std::atomic<bool> audioRunning {false};
|
||||
};
|
||||
|
||||
std::mutex g_mutex;
|
||||
@@ -116,6 +121,53 @@ VstIntPtr VSTCALLBACK hostAudioMasterImpl (AEffect* effect, int32 opcode, int32
|
||||
}
|
||||
}
|
||||
|
||||
// T12: audio thread callback — effProcessEvents (MIDI drain tu SPSC) +
|
||||
// processReplacing. Chay tren WASAPI render thread: KHONG lock, KHONG cap phat.
|
||||
bool vst2AudioProcess (const sonicforge::MidiEvent* events, int32 eventCount,
|
||||
float* outL, float* outR, int32 frames, void* userdata)
|
||||
{
|
||||
Instance* inst = static_cast<Instance*> (userdata);
|
||||
if (!inst || !inst->effect)
|
||||
return false;
|
||||
|
||||
if (eventCount > 0)
|
||||
{
|
||||
int32 n = eventCount < 256 ? eventCount : 256;
|
||||
VstMidiEvent midi[256];
|
||||
VstEvent* ptrs[256];
|
||||
struct VstEventsBuf { int32 numEvents; intptr_t reserved; VstEvent* events[256]; };
|
||||
VstEventsBuf buf = {};
|
||||
buf.numEvents = n;
|
||||
for (int32 i = 0; i < n; ++i)
|
||||
{
|
||||
VstMidiEvent& m = midi[i];
|
||||
std::memset (&m, 0, sizeof (m));
|
||||
m.type = kVstMidiType;
|
||||
m.byteSize = static_cast<int32> (sizeof (VstMidiEvent));
|
||||
m.deltaFrames = 0;
|
||||
m.flags = kVstMidiEventIsRealtime;
|
||||
m.midiData[0] = static_cast<char> ((events[i].noteOn ? 0x90 : 0x80) |
|
||||
(events[i].channel & 0x0F));
|
||||
m.midiData[1] = static_cast<char> (events[i].pitch & 0x7F);
|
||||
m.midiData[2] = events[i].noteOn
|
||||
? static_cast<char> (static_cast<int> (events[i].velocity * 127.0f) & 0x7F)
|
||||
: 0;
|
||||
ptrs[i] = reinterpret_cast<VstEvent*> (&m);
|
||||
buf.events[i] = ptrs[i];
|
||||
}
|
||||
inst->effect->dispatcher (inst->effect, effProcessEvents, 0, 0,
|
||||
reinterpret_cast<VstEvents*> (&buf), 0.0f);
|
||||
}
|
||||
|
||||
if (inst->effect->processReplacing)
|
||||
{
|
||||
float* outs[2] = { outL, outR };
|
||||
inst->effect->processReplacing (inst->effect, nullptr, outs, frames);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
@@ -244,6 +296,9 @@ __declspec (dllexport) int32 SF_VST2_Close (int32 handle, char* err, int32 err_c
|
||||
return -1;
|
||||
}
|
||||
Instance* inst = it->second.get ();
|
||||
// T12: dung audio loop (join render thread) truoc khi pha huy plugin.
|
||||
inst->audioRunning.store (false, std::memory_order_release);
|
||||
inst->audio.stop ();
|
||||
if (inst->effect)
|
||||
{
|
||||
{
|
||||
@@ -320,6 +375,18 @@ __declspec (dllexport) int32 SF_VST2_SendNoteOn (int32 handle, int32 channel, in
|
||||
if (!inst->effect)
|
||||
return -2;
|
||||
|
||||
// T12: audio loop dang chay -> day vao SPSC (audio thread xu ly).
|
||||
if (inst->audioRunning.load (std::memory_order_acquire))
|
||||
{
|
||||
sonicforge::MidiEvent ev = {};
|
||||
ev.channel = channel;
|
||||
ev.pitch = pitch;
|
||||
ev.velocity = static_cast<float> (velocity) / 127.0f;
|
||||
ev.noteOn = true;
|
||||
inst->audio.pushMidi (ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char midiData[4] = {0};
|
||||
midiData[0] = static_cast<char> (0x90 | (channel & 0x0F));
|
||||
midiData[1] = static_cast<char> (pitch & 0x7F);
|
||||
@@ -352,6 +419,18 @@ __declspec (dllexport) int32 SF_VST2_SendNoteOff (int32 handle, int32 channel, i
|
||||
if (!inst->effect)
|
||||
return -2;
|
||||
|
||||
// T12: audio loop dang chay -> day vao SPSC.
|
||||
if (inst->audioRunning.load (std::memory_order_acquire))
|
||||
{
|
||||
sonicforge::MidiEvent ev = {};
|
||||
ev.channel = channel;
|
||||
ev.pitch = pitch;
|
||||
ev.velocity = 0.0f;
|
||||
ev.noteOn = false;
|
||||
inst->audio.pushMidi (ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char midiData[4] = {0};
|
||||
midiData[0] = static_cast<char> (0x80 | (channel & 0x0F));
|
||||
midiData[1] = static_cast<char> (pitch & 0x7F);
|
||||
@@ -464,4 +543,63 @@ __declspec (dllexport) int32 SF_VST2_GetParam (int32 handle, int32 paramIndex, d
|
||||
return 0;
|
||||
}
|
||||
|
||||
// T12: bat native audio loop (SPSC + WASAPI) cho instance nay. Audio thread
|
||||
// goi vst2AudioProcess (effProcessEvents + processReplacing) moi block.
|
||||
__declspec (dllexport) int32 SF_VST2_AudioStart (int32 handle, int32 sampleRate,
|
||||
int32 blockSize, char* err, int32 err_cap)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_mutex);
|
||||
auto it = g_instances.find (handle);
|
||||
if (it == g_instances.end ())
|
||||
return -1;
|
||||
Instance* inst = it->second.get ();
|
||||
if (!inst->effect)
|
||||
return -2;
|
||||
inst->audioRunning.store (true, std::memory_order_release); // truoc khi thread chay
|
||||
if (!inst->audio.start (sampleRate, blockSize, &vst2AudioProcess, inst, err, err_cap))
|
||||
{
|
||||
inst->audioRunning.store (false, std::memory_order_release);
|
||||
return -3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec (dllexport) int32 SF_VST2_AudioStop (int32 handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_mutex);
|
||||
auto it = g_instances.find (handle);
|
||||
if (it == g_instances.end ())
|
||||
return -1;
|
||||
it->second->audioRunning.store (false, std::memory_order_release);
|
||||
it->second->audio.stop ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec (dllexport) int32 SF_VST2_AudioUnderruns (int32 handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_mutex);
|
||||
auto it = g_instances.find (handle);
|
||||
if (it == g_instances.end ())
|
||||
return -1;
|
||||
return it->second->audio.underruns ();
|
||||
}
|
||||
|
||||
__declspec (dllexport) int32 SF_VST2_AudioLatency (int32 handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_mutex);
|
||||
auto it = g_instances.find (handle);
|
||||
if (it == g_instances.end ())
|
||||
return -1;
|
||||
return it->second->audio.latencySamples ();
|
||||
}
|
||||
|
||||
__declspec (dllexport) int32 SF_VST2_AudioBlocks (int32 handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_mutex);
|
||||
auto it = g_instances.find (handle);
|
||||
if (it == g_instances.end ())
|
||||
return -1;
|
||||
return it->second->audio.blocksRendered ();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
Reference in New Issue
Block a user