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 SuperCollider