Using the Bela core with other programs

In order to use Bela as an audio backend for another program, you can take advantage of libbela, a library that contains all the core code of Bela.

Setup

Just do make lib to build libbela and libbelaextra. The shared and static version of the libraries will be in Bela/lib. You would normally only need libbela for your programs, unless you use some of Bela’s extended features (Scope, WriteFile, Midi, …), which are included only in libbelaextra. The header files for libbela live in Bela/include. The following should be added to your compiler command line to find the include:

-I/root/Bela/include

Pass to your linker these:

-L/root/Bela/lib -lbela

Additionally, only if you want to use the Xenomai interface directly from the host program, you will need to link in a number of extra libraries, needed by Xenomai, which you will find with

    /usr/xenomai/bin/xeno-config --ldflags --skin=posix --no-auto-init

which will normally return

-Wl,--no-as-needed -Wl,@/usr/xenomai/lib/cobalt.wrappers -Wl,@/usr/xenomai/lib/modechk.wrappers    -L/usr/xenomai/lib -lcobalt -lmodechk -lpthread -lrt

note that the two .wrappers files are used to tell the linker to replace the calls to a number of POSIX functions with the corresponding Xenomai version. Now, this may or may not be what you want. For instance, in the Bela core code we disable these wrappers and we explicitly call the xenomai service only when we need it. This can be done by prefixing the function call with __wrap_, e.g. pthread_create() -> __wrap_pthread_create(). I seem to remember that using C++ threads or mutexes makes the wrappers unusable (because these would require the libstdc++ shared library to be re-linked with the wrappers or something similar). Follow instructions here to debug mode switches.

Usage

Your program will have to make the calls to the Bela API, pretty much similarly to how Bela/core/default_main.cpp does, here is a stripped-down version of it:

#include <Bela.h>

/*
/// define somewhere functions that correspond to the signature of:
extern "C"
{
int (*)(BelaContext*, void*); // setup, return 1 for success, 0 for error
void (*)(BelaContext*, void*); // render
void (*)(BelaContext*, void*); // cleanup
}
*/

void interrupt_handler(int)
{
        gShouldStop = 1;
}

main()
{
	// Set default settings
	BelaInitSettings settings;	// Standard audio settings
	Bela_defaultSettings(&settings);
        // you must have defined these function pointers somewhere and assign them to `settings` here.
        // only `settings.render` is required.
	settings.setup = mysetup;
	settings.render = myrender;
	settings.cleanup = mycleanup;

	// Initialise the PRU audio device
	if(Bela_initAudio(&settings, 0) != 0) {
                fprintf(stderr, "Error: unable to initialise audio");
		return -1;
	}

	// Start the audio device running
	if(Bela_startAudio()) {
		fprintf(stderr, "Error: unable to start real-time audio");
		// Stop the audio device
		Bela_stopAudio();
		// Clean up any resources allocated for audio
		Bela_cleanupAudio();
		return -1;
	}

	// Set up interrupt handler to catch Control-C and SIGTERM
	signal(SIGINT, interrupt_handler);
	signal(SIGTERM, interrupt_handler);

	// Run until told to stop
	while(!gShouldStop) {
		usleep(100000);
	}

	// Stop the audio device
	Bela_stopAudio();

	// Clean up any resources allocated for audio
	Bela_cleanupAudio();

	// All done!
	return 0;
}

Testing

Run your program, inspect the values of the BelaContext* passed to setup() and make sure they match those you request through Bela_defaultSettings(): if you request some invalid settings, they will be fixed by the time setup() is called. While the program is running, check cat /proc/xenomai/sched/stat (or cat /proc/xenomai/stat on older images) to make sure your audio thread does not have an increasing number of mode switches.

Advanced

A number of parameters in BelaInitSettings properties could be particularly useful when using Bela as an audio backend for other programs, shown here with the suggested values:

int highPerformanceMode = 1;
int interleave = // 0 or 1 depending on the data format of your program
int analogOutputsPersist = 0; // persisting analog outputs would require extra resources but is pointless when the analog out streams are treated as audio channels, which is the common approach for many programs
int uniformSampleRate = 1; // most audio programs only allow one sample rate for all the input stream. Enabling this will resample the analog channels to match the audio channels in the backend.

see further details here.

In order to control digital I/O channels at message rate, you may find useful the DigitalChannelManager class, which we use for instance here.

Examples

Audio backend for Pd note: this is NOT the way Pd is currently run on Bela.

Audio backend for jalv

Audio backend for SuperCollider