Learn Getting and Setting Parameters in C++

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

Getting and Setting Parameters C++

A CoreObject has multiple methods for working with parameters. For instance, you can use the getNumParameters method to get a count of the parameters available in your exported RNBO patch. This could be useful when doing something like printing out each individul parameter's name and id. The parameter corresponds to a param object in your original RNBO patcher.

// Print the names of all the top-level parameters in the device.
#include <iostream>
#include "RNBO.h"

int main(int argc, const char * argv[]) {

  RNBO::CoreObject rnboObject;

  int n = rnboObject.getNumParameters();
  for (int i = 0; i < n; i++) {
    const char *id = rnboObject.getParameterId(i);
    const char *name = rnboObject.getParameterName(i);

    // This if statement filters out subpatcher parameters,
    // only printing the name of top level parameters. This
    // isn't necessary, so comment out this if statement if 
    // you want to print out all parameters.
    if (strstr(id, "/") == nullptr) {
      std::cout << i << ":\t" << name << "\n";
    }
  }

  return 0;
}

Changing Parameter Values

RNBO::CoreObject also exposes two functions called getParameterValue and setParameterValue, which can be used to get and set parameter values. These functions take a parameter index as their first argument, which you can retrieve using the getParameterIndexForID function. An optional third argument to setParameterValue lets you specify the time at which to update the parameter value, in case you want to schedule an update in the future. You can use RNBO::RNBOTimeNow to update the parameter as soon as possible, or you can use the RNBO::CoreObject function getCurrentTime to retrieve the current time in milliseconds, and then pick a time relative to that.

// Move the pitch parameter up by 5
int parameterIndex = rnboObject.getParameterIndexForID("pitch");
double currentPitch = rnboObject.getParameterValue(parameterIndex);

// Schedule an update for one second from now
rnboObject.setParameterValue(parameterIndex, currentPitch + 5, rnboObject.getCurrentTime() + 1000);

Listening to Parameter Changes

To listen to parameter changes, one must write a concrete subclass of the EventHandler virtual class. For parameters, the two important virtual methods of this class are eventsAvailable and handleParameterEvent. In a typical use case, the application will call process on the RNBO core object from the audio thread. If a parameter is updated, it will generate a ParameterEvent, calling eventsAvailable from the audio thread. At this point, the application should defer a call to drainEvents to the main thread (or whatever thread is handling ParameterEvents through handleParameterEvent). Finally, the simplest way to use the event handler is to pass an instance to the core object constructor. When we use this form of the core object constructor, we must also create an interface to the patcher using a factory function.

using namespace RNBO;

class CustomParameterHandler : EventHandler {

  // This should be called from the main thread, or some other non-audio thread
  void doDrainEvents() {
    drainEvents();
  }

  void eventsAvailable() override {
    // This function triggers some mechanism for calling doDrainEvents from the main thread
    deferDrainEvents();
  }

  void handleParameterEvent(const ParameterEvent& event) override {
    // Do something with the parameter events
  }
};

PatcherFactoryFunctionPtr patcherFactoryFunc = GetPatcherFactoryFunction(Platform().get());
LoggingParameterHandler handler;
auto patcher = patcherFactoryFunc();
CoreObject rnboObject(UniquePtr<PatcherInterface>(patcher), (EventHandler *)&handler);

In a very simple test application, it might not be necessary to do any kind of threading. In this case we can explicitly set the type of the ParameterInterface using the createParameterInterface method of the RNBO core object. We can choose between the default ParameterEventInterface::MultiProducer, where multiple threads can all set parameters, the optimized ParameterEventInterface::SingleProducer which only allows one thread to set parameters, and ParameterEventInterface::NotThreadSafe, which doesn't introduce any notion of threading. With this type, eventsAvailable will never be called.

#include <iostream>
#include "RNBO.h"

#define __NOT_THREAD_SAFE

using namespace RNBO;

class LoggingParameterHandler : EventHandler {

  void eventsAvailable() override {
#ifndef __NOT_THREAD_SAFE
    drainEvents();
#endif
  }

  void handleParameterEvent(const ParameterEvent& event) override {
    fprintf(
      stdout, 
      "ParameterEvent: parameterIndex=%d time=%.3f value=%.4f source=%p\n",
      event.getIndex(),
      event.getTime(),
      event.getValue(),
      event.getSource()
    );
  }
};

int main(int argc, const char * argv[]) {
  LoggingParameterHandler handler;
  CoreObject rnboObject;

#ifndef __NOT_THREAD_SAFE
  auto interface = 
        rnboObject.createParameterInterface(ParameterEventInterface::MultiProducer, (EventHandler *) &handler);
#else 
  auto interface = 
        rnboObject.createParameterInterface(ParameterEventInterface::NotThreadSafe, (EventHandler *) &handler);
#endif

  rnboObject.prepareToProcess(44100, 64);
  SampleValue** inputs = nullptr;
  SampleValue** outputs = new SampleValue*[1];
  outputs[0] = new double[64];

  while (true) {
    rnboObject.process(inputs, 0, outputs, 1, 64);
    rnboObject.setParameterValue(0, 74);
  }

  delete [] outputs[0];
  delete [] outputs;

  return 0;
}

Parameter Scaling and Normalization

The RNBO core object has a getParameterValue method for getting parameter values. In addition, it also has the methods getParameterNormalized and setParameterValueNormalized allowing you to get/set the parameter value using a range between 0 and 1. Calling setParameterValueNormalized with a value of 0 will set the parameter to its minimum value, and with 1 will set the parameter to its maximum value.

RNBO parameters may have a nonlinear scaling value applied to their normalization—either a simple exponent or else a custom normalization expression as defined in the original RNBO patcher. The parameter supplies functions that will take this custom scaling into account.