Learn Multiple RNBO Devices in one C++ Project

Getting Started

Welcome to RNBO

Quickstart

RNBO Basics

Key Differences

Why We Made RNBO

Coding Resources

Fundamentals

Audio IO

Messages to rnbo~

Using Parameters

MIDI in RNBO

Messages and Ports

Polyphony and Voice Control

Audio Files in RNBO

Using Buffers

Using the FFT

Export Targets

Export Targets Overview

VST/AudioUnit
Max External Target
Raspberry Pi Target
The Web Export Target
The C++ Source Code Target

Code Export

Working with JavaScript
Working with C++

How to Include RNBO in Your C++ Project

RNBO Helper Types

Getting and Setting Parameters in C++

Sending and Receiving Messages in C++

Sending and Receiving MIDI in C++

Multiple RNBO Devices in one C++ Project

Working with Presets in C++

Loading File Dependencies

Special Topics

Sample Accurate Patching
Scala and Custom Tuning

RNBO and Max for Live

RNBO Raspberry Pi OSCQuery Runner

Metadata

Export Description

Raspberry Pi GPIO

Updating the RNBO Package

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.

multiple-rnbo-devices-in-one-cpp-01.png

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)));