Learn Using Buffers

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++

Using Buffers

Data buffers in RNBO allow you to store, manipulate and retrieve information in memory. From loading external files, such as audio files or wavetables, to live sampling and creating custom dynamic data structures, buffers are an important part of the RNBO toolkit.

buffer~ vs. data

The two primary objects that can be used to create data buffers are buffer~ and data. In each case, the objects require a single argument which is the name of the buffer to allocate into memory.

In most ways the two objects are interchangable. Both create a buffer in memory that can be accessed and manipulated by associated objects and methods in the rnbo~ patcher (see below); both can be filled either internally (using the fill attribute for example) or externally via a file or url; both are exposed as external datarefs in the exported code that allow files to be loaded into the memory buffers. The primary difference between them is how they interact with Max.

buffer~

The buffer~ object is usually the simpler of the two objects to use because it integrates directly with Max. Any buffer~ object in a rnbo~ patcher automatically becomes associated with an identical Max buffer. If a buffer~ already exists in Max with the same name, they both share the same data. Any changes made to either version will propagate to the other. This means you can load a soundfile into Max and read it from a rnbo~ patch or preview changes made to a buffer~ in rnbo~ on the Max side.

One major benefit from a usability perspective is that you can double-click a buffer~ object in a rnbo~ patch and see a waveform preview of the buffer contents.

Because a buffer~ object shares memory with a corresponding Max buffer, the buffer~ object always uses 32-bit, floating point memory. If you need 64-bit memory, use data instead.

data

The data object behaves the same as buffer~, except that its memory is not shared with a Max buffer. Because of this, data does not support waveform previews or communication with Max buffers. However, data can be made internal by setting @external 0, in which case the data object will not show up among the exposed DataRefs of the RNBO export. This can be useful when you want to prevent access to an internally generated buffer. For example, you might want to create a wavetable oscillator using a particular formula that shouldn't be altered after export.

The data object defaults to creating float64 memory buffers, which creates added precision for the stored data, but can be set to float32 using the type attribute.

Loading files into buffers

In RNBO, samples can be loaded into buffer~ or data objects using either the file or url attributes. Note that a data object set to @external 0 can not be used for file loading.

The @file attribute allows you to specify a file on disk and fill the memory buffer with the data in the file. Files in Max's file path can be loaded by name or a full path can be supplied. During export, any files specified by the file attribute can be automatically copied over to the exported platform or project folder by checking the Copy File Dependencies configuration option.

Specifying a valid url via the @url attribute will also fill the associated buffer.

When a buffer is filled using either method it will automatically set the size of the buffer to match the total number of samples in the referenced file.

Setting the size of a buffer

When creating a buffer it is important to determine an appropriate size to fit your needs. For example, if you are creating a wavetable osciallator you might only need a relatively small number of samples, like 256 or 512. A live looper on the other hand could need as little as a few seconds of space in memory to a whole lot more.

If a buffer is not being filled externally (via file or url), you will usually want to set a specific size so that you can manage memory allocation and control where in the buffer you are writing. If no size is specified, buffers will default to 1000 samples in length.

Size can be set in various formats depending on specific needs. For size in samples, use the size attribute. For size in vectors, use vectorsize, or for millisecond values use sizems. Additionally, the size attribute accepts special arguments that allow for setting the size based on the audio device driver. These are samplerate and vectorsize. For example, size samplerate*2 will create a buffer that is exactly two seconds long in samples, regardless of the actual sample rate of the audio device driver being used.

Referencing a buffer with other objects

Similar to the buffer~ object in Max, buffer-enabled objects such as groove~, and poke~ can reference the memory buffer of any buffer~ or data object by name. For example, a buffer~ foo @size 512 can be operated on by bufferop~ foo and read from by peek~ foo.

Within the RNBO patcher, you can also reference any named Max buffer~ object in Max by making a same-named buffer~, groove~ or wave~ object.

Behavior with no buffer~ or data

Any object that references named data, like groove~ or wave~, is enough to create a data reference. That means that if there is no buffer~ or data object in your RNBO patch, the RNBO patch will by default create a link to a Max buffer with the same name, and an externally available DataRef will be added to the exported code. Create a buffer~ or data object with the same name to change this behavior, for example by making the data internal, or setting its size.

However, the situation is different if an object such as groove~ or wave~ references multiple buffers. If a single object such as groove~ or wave~ references a list of buffer~ or data objects that have not been created, a code generation error will occur.

multibuffer~

The multibuffer~ object allows you to load multiple files at runtime, then select which of the buffers to expose to other objects using a numerical index.

This can be especially useful in polyphonic contexts where you want each voice to reference a different buffer. Simply connect the output of the voice object to set the index.