Fundamentals
Export Targets
Code Export
Patcher UI
Special Topics
RNBO Raspberry Pi OSCQuery Runner
Multiple RNBO Devices in one C++ Project
Sometimes you need to work with multiple exported devices in one C++ project, or even one C++ file. We'll use CMake here for demonstration, but the steps are the same for any development environment. At a high level:
- Use the Codegen: Classname configuration property of the C++ Source Code Export to choose a unique classname for each exported patcher.
- Define the preprocessor macro
RNBO_NO_PATCHERFACTORY
to disable the declaration of the global patcher factory - Get a factory function for each device, which you can use to create a core object for each one.
First, when exporting your patch, use the Export Name and Codegen: Classname configuration options to choose a unique filename and classname for each exported patcher.
After exporting both files, you might have a folder structure like this one...
Now modify your CMakeLists.txt file
to include both sources, and to define the RNBO_NO_PATCHERFACTORY
preprocessor macro.
cmake_minimum_required(VERSION 3.10) # Set the C++ standard to at least C++11, which is needed for RNBO set (CMAKE_CXX_STANDARD 11) # Set the project name project(RNBOCommandLine) # Add the main executable as well as the RNBO sources add_executable(RNBOCommandLine main.cpp rnbo_effect.cpp rnbo_synth.cpp rnbo/RNBO.cpp) # Add a preprocessor macro to disable the convenience definition of the global patcher factory target_compile_definitions(RNBOCommandLine PRIVATE RNBO_NO_PATCHERFACTORY) # Include the RNBO headers target_include_directories(RNBOCommandLine PRIVATE rnbo rnbo/common)
Finally, create a main.cpp file that declares a factory function for each device class. The name of the factory function will be XXXFactoryFunction
, where XXX
is the value of the Codegen: Classname configuration option.
#include <iostream>
#include "RNBO.h"
using namespace RNBO;
// First you have to expose the factory functions with extern “C” declarations:
extern "C" PatcherFactoryFunctionPtr rnbosynthFactoryFunction(PlatformInterface* platformInterface);
extern "C" PatcherFactoryFunctionPtr rnboeffectFactoryFunction(PlatformInterface* platformInterface);
CoreObject rnboEffectObject;
int main(int argc, const char * argv[]) {
auto synthPatcherInterface = rnbosynthFactoryFunction(Platform::get())();
auto effectPatcherInterface = rnboeffectFactoryFunction(Platform::get())();
// You can either construct the CoreObject directly with
// the PatcherInterface you get from the factory function
CoreObject rnboSynthObject((UniquePtr<PatcherInterface>(synthPatcherInterface)));
rnboSynthObject.prepareToProcess(44100, 64);
// Or you have the CoreObject already declared somewhere else
// and call setPatcher with the PatcherInterface you get from
// the factory function
rnboEffectObject.setPatcher((UniquePtr<PatcherInterface>(effectPatcherInterface)));
rnboEffectObject.prepareToProcess(44100, 64);
// Make buffers to hold your inputs and outputs
SampleValue** dummyInputs = nullptr;
SampleValue** inputs = new SampleValue*[1];
inputs[0] = new double[64];
SampleValue** outputs = new SampleValue*[1];
outputs[0] = new double[64];
// Process the synth, feed it to the effect, print the result
rnboSynthObject.process(dummyInputs, 0, inputs, 1, 64);
rnboEffectObject.process(inputs, 1, outputs, 1, 64);
for (int i = 0; i < 64; i++) {
std::cout << outputs[0][i] << "\n";
}
delete [] inputs[0];
delete [] outputs[0];
delete [] inputs;
delete [] outputs;
return 0;
}
You can simply use them to instantiate a core object. If you already have a core object, you can also call the setPatcher
function, passing the PatcherInterface
that comes from the factory function.
CoreObject rnboeffectFactoryFunction;
rnboeffectFactoryFunction.setPatcher((UniquePtr<PatcherInterface>(effectPatcherInterface)));