83 lines
2.9 KiB
C++
83 lines
2.9 KiB
C++
// 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;
|
|
}
|