// native_bridge/juce_fx/JuceFxEngine.cpp // Implementation: AudioProcessorGraph với input/output node — chain rỗng G1. #include "JuceFxEngine.h" #include #include #include #include struct JuceFxEngine::Impl { juce::AudioProcessorGraph graph; juce::AudioBuffer buf; uint32_t sampleRate = 0; uint32_t blockSize = 0; bool prepared = false; }; JuceFxEngine::JuceFxEngine() : impl_(std::make_unique()) {} 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; }