G1: JUCE engine SHM chain rong POC - probe pass, khong lag them

This commit is contained in:
2026-08-28 12:35:37 +07:00
parent d7dd626dd5
commit 9c0d35def4
13 changed files with 517 additions and 33 deletions
+82
View File
@@ -0,0 +1,82 @@
// native_bridge/juce_fx/JuceFxEngine.cpp
// Implementation: AudioProcessorGraph với input/output node — chain rỗng G1.
#include "JuceFxEngine.h"
#include <juce_audio_processors/juce_audio_processors.h>
#include <algorithm>
#include <array>
#include <vector>
struct JuceFxEngine::Impl {
juce::AudioProcessorGraph graph;
juce::AudioBuffer<float> buf;
uint32_t sampleRate = 0;
uint32_t blockSize = 0;
bool prepared = false;
};
JuceFxEngine::JuceFxEngine() : impl_(std::make_unique<Impl>()) {}
JuceFxEngine::~JuceFxEngine() { shutdown(); }
void JuceFxEngine::shutdown() {
if (impl_->prepared) {
impl_->graph.releaseResources();
impl_->graph.clear();
impl_->prepared = false;
impl_->sampleRate = 0;
impl_->blockSize = 0;
}
}
void JuceFxEngine::prepare(uint32_t sampleRate, uint32_t blockSize) {
if (impl_->prepared && impl_->sampleRate == sampleRate &&
impl_->blockSize == blockSize)
return; // không đổi — giữ graph
shutdown();
impl_->sampleRate = sampleRate;
impl_->blockSize = blockSize;
impl_->graph.setPlayConfigDetails(2, 2, (double)sampleRate, (int)blockSize);
// Chain rỗng G1: audio input -> audio output (passthrough). G2: chèn
// plugin node giữa hai node này theo fx_chain.
auto& g = impl_->graph;
const auto inNode = g.addNode(std::make_unique<
juce::AudioProcessorGraph::AudioGraphIOProcessor>(
juce::AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode));
const auto outNode = g.addNode(std::make_unique<
juce::AudioProcessorGraph::AudioGraphIOProcessor>(
juce::AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode));
using NCh = juce::AudioProcessorGraph::NodeAndChannel;
using Conn = juce::AudioProcessorGraph::Connection;
for (int ch = 0; ch < 2; ++ch) {
NCh src; src.nodeID = inNode->nodeID; src.channelIndex = ch;
NCh dst; dst.nodeID = outNode->nodeID; dst.channelIndex = ch;
g.addConnection(Conn(src, dst));
}
g.prepareToPlay((double)sampleRate, (int)(blockSize * 4)); // batch tối đa 4 block
impl_->buf.setSize(2, (int)(blockSize * 4), false, false, true);
impl_->prepared = true;
}
void JuceFxEngine::process(float* inL, float* inR, uint32_t n) {
if (!impl_->prepared || n == 0) return;
auto& b = impl_->buf;
b.setSize(2, (int)n, false, false, true);
b.copyFrom(0, 0, inL, (int)n);
b.copyFrom(1, 0, inR, (int)n);
juce::MidiBuffer midi;
impl_->graph.processBlock(b, midi);
// G1 passthrough: copy kết quả về buffer đầu vào (in-place như contract).
const auto* outL = b.getReadPointer(0);
const auto* outR = b.getReadPointer(1);
std::copy(outL, outL + n, inL);
std::copy(outR, outR + n, inR);
}
uint32_t JuceFxEngine::latencySamples() const {
return impl_->prepared ? (uint32_t)impl_->graph.getLatencySamples() : 0u;
}