Learn RNBO Helper Types

Getting Started

Welcome to RNBO

Quickstart

RNBO Basics

Key Differences

Why We Made RNBO

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

RNBO Helper Types

Before working with RNBO, there's a handful of helper types that deserve special mention. Having some familiarity with these types will make working with RNBO much easier.

UniquePtr

The notion of a unique pointer is borrowed heavily from the c++11 std::unique_ptr structure. The core concept is essentially the same: UniquePtr is a smart pointer that manages another object. When the UniquePtr goes out of scope, the object itself is destroyed. UniquePtr comes with a convenience function make_unique, a templated function that constructs a new object and a UniquePtr to manage that object at the same time.

using namespace RNBO;

std::cout << "Starting the program\n";
CoreObject rnboObject;

if (true) {
    ConstPresetPtr newPreset = rnboObject.getPresetSync();

    // This creates a new Preset, as well as a UniquePtr to manage that preset
    UniquePtr<Preset> uniquePreset = make_unique<Preset>();
    copyPreset(*newPreset, *uniquePreset);

    // No need to worry about deleting the Preset. As soon as the uniquePreset pointer
    // goes out of scope, it will delete the managed Preset object as well.

    // Or, we could call std::move on the uniquePreset, handing off management of the
    // pointer to another part of the program
    rnboObject.setPreset(std::move(uniquePreset));
}

// In any case, uniquePreset is no longer memory that this part of the program
// needs to manage

One important thing to keep in mind is that because a UniquePtr deletes its managed object as soon as the UniquePtr goes out of scope, any function that returns a UniquePtr will delete its return value immediately if the return value is unused. Occasionally this can cause some slightly surprising results. Consider the case where the user calls createParameterInterface to attach an EventHandler to a RNBO core object.

ConcreteEventHandler handler;

if (true) {
    // interface is a UniquePtr. When it goes out of scope, the managed 
    // ParameterInterface object will be deleted.
    auto interface = rnboObject.createParameterInterface(
                         ParameterEventInterface::MultiProducer, 
                         (EventHandler *) &handler
                     ); 
}

// Now that UniquePtr is out of scope, the ParameterInterface has been deleted. 
// The EventHandler will no longer receive events.

Even if the returned interface value is unused, it must be assigned to a variable or kept in scope for the EventHandler to receive events. It's a subtle point, but helpful to keep in mind, in case you're not receiving events when you expect to.

There are some key differences between std::unique_ptr and UniquePtr. There's no copy constructor or assignment operator for UniquePtr, which makes UniquePtr a more limited, but also much simpler alternative. In most cases, it's enough to understand what a UniquePtr is trying to achieve, and to know how to interface with RNBO API functions that require a UniquePtr instead of an unmanaged pointer (RNBO::CoreObject::setPreset is a perfect example).

RNBO::list

Similar to Max, RNBO has a notion of a list. Lists in RNBO consist of numbers only, but it's possible to treat them somewhat similar to a std::vector in the C++ standard library. You can create, iterate, and modify a RNBO::list as you'd expect.

RNBO::list mylist;
mylist.push(1);
mylist.push(2);
mylist.push(3);

for (int i = 0; i < mylist.length; i++) {
  if (i != 0) std::cout << ", ";
  std::cout << mylist[i];
}
std::cout << "\n";

// Prints 1, 2, 3

mylist[0] = 4;
mylist = mylist.reverse();

for (int i = 0; i < mylist.length; i++) {
  if (i != 0) std::cout << ", ";
  std::cout << mylist[i];
}
std::cout << "\n";

// Prints 3, 2, 4

Probably the most common way to use lists is to send messages to a RNBO inport or in object. In this case, the list must be wrapped in a UniquePtr and then handed off to the RNBO core object using std::move.

using namespace RNBO;

auto mylist = make_unique<list>();
(*mylist).push(1);
(*mylist).push(2);
(*mylist).push(3);
rnboObject.sendMessage(TAG("in1"), std::move(mylist), TAG(""));