Hi Bela users.
I'm fairly new to programming in the Bela IDE and to the Bela API. What is the purpose/idea of the *userData that is passed around.
Best, Hjalte
Hi Bela users.
I'm fairly new to programming in the Bela IDE and to the Bela API. What is the purpose/idea of the *userData that is passed around.
Best, Hjalte
That is a opaque pointer used to pass arguments around. That pointer is passed to the Bela backend when calling the int Bela_initAudio(BelaInitSettings *settings, void *userData)
function. Any Bela project that does not have a custom main.cpp
file uses under the hood core/default_main.cpp
, which contains the call to Bela_init()
, and passes a NULL
pointer as userData
. This means that if you do not have a custom main.cpp
, then userData
is not used.
Some of the example projects provide a custom main()
function and use userData
to pass some data retrieved from the command-ilne to the setup()
function:
./04-Audio/FFT-phase-vocoder/render.cpp: gSampleData = *(SampleData *)userData;
./10-Instruments/d-box/render.cpp: int oscBankHopSize = *(int *)userData;
./11-Extras/second-pru/render.cpp: int pruNumber; // comes from userData
./11-Extras/second-pru/render.cpp: // Which PRU to use is in userData, assuming this project
./11-Extras/second-pru/render.cpp: if(userData == 0) {
./11-Extras/second-pru/render.cpp: pruNumber = *((int *)userData);
./11-Extras/second-pru/render.cpp: int pruNumber = *((int *)userData);
./11-Extras/userdata/render.cpp: if(userData != 0)
./11-Extras/userdata/render.cpp: gFrequency = *(float *)userData;
./terminal-only/filter-FIR/render.cpp: gSampleData = *(SampleData *)userData;
./terminal-only/filter-IIR/render.cpp: gSampleData = *(SampleData *)userData;
./terminal-only/samples/render.cpp: gSampleData = *(SampleData *)userData;
However, similar results could be achieved - in the context of a Bela project - using global variables.
The most powerful use of userData
, however, is when embedding Bela into an other application. For instance, in the code for the Bela backend for SuperCollider, userData
is a pointer to a driver object instance:
Bela_initAudio(&settings, this)
at that point, every time the audio callback function (sc_belaRender()
) is called, it is passed a reference to the driver object and it can (after appropriate casting) access its members:
void sc_belaRender(BelaContext *belaContext, void *userData)
{
SC_BelaDriver *driver = (SC_BelaDriver*)userData;
driver->BelaAudioCallback(belaContext);
}
Very clarifying. Thank you
giuliomoro
From what I understand here, it is possible to modify the userdata in the main.cpp during runtime (say, reading analog inputs in each iteration). So, does sc_belaRender pass the latest userdata to the render function? Or does it pass only the userdata there was during initialization?
This could be handy because I could implement all the extra logic in the main.cpp and then pass all the parameters through userdata to render in runtime.
The userData
that is passed to setup()
, render()
, cleanup()
is the one that is set in the call to Bela_initAudio
: this is normally set just once, typically from the main()
function in core/default_main.cpp
file.
If you are calling Bela_initAudio()
multiple times (e.g.: if you are start/stopping the audio multiple times), then you can change that userData
for every call.
However, if your objective is to use it to access a portion of memory that is changed somewhere else in the code, keep in mind that the content ofuserData
can be of any type, it is just casted to a void*
when calling Bela_initAudio()
and has to be casted back to the same type in the callback functions. This way, if it actually is a pointer to a location in memory, and you can change the content of the memory it points to, if you want to implement a way of change the data it gives access to for each call to render()
.
do you think we could add a setUserData() method?
(it simply needs to set gUserData, which is currently not exposed)
I quite like the idea of using it to avoid globals, and so creating a data structure in setup(), and then calling it, and knowing that it would be available in render() and cleanup().
a setUserData method, would avoid the requirement to copying default_main.cpp, which id prefer to avoid incase subsequent bela release 'change/improve it'
seems like a good idea, wanted to do something similar for gShouldStop
as well. Do you have a Pull Request ready?
done here. This stuff is going to be merged intomaster
next week. However, I thought that maybe the best place to set that would be as a member of the BelaInitSettings
structure, which can be set by the user if the define a Bela_userSettings()
function, so I may move to that solution later on.