Hi, I am not sure I understand what exact result you’re after, but I’ll try to explain as much as I can.
-
Attributes are just placeholders that will be replaced with the selected value by the Patcher’s code generator. So if I create an attr_giraffe in one object and set its value to, say, “4” or “GPIOA,1”, the code generator will literally parse that object when you click on “live”, and it will replace all attr_giraffe substrings it can find with 4 or GPIOA,1. Note that this replacement applies only inside the object that contains the attribute.
-
If I am not mistaken, defines should be valid across the whole patch. So if you do
#define BANANA attr_giraffe
other objects in the patch should technically be able to pick up the define BANANA, you’re correct there.
BUT there is a certain instantiation and execution order of objects, from left to right, and line by line from top to bottom. (IIRC the object’s title bar is seen as an anchor for the object’s position).
So if you go like this:
You get the following error:
If we look at the auto generated xpatch.cpp (in home/[user]/ksoloti/build, or home/[user]/ksoloti/[version]/build since 1.1.0), we can see what has really happened:
non-working xpatch.cpp
/*
* Generated using Ksoloti Patcher v1.1.0-4-g7ba2ed26 on Windows 10
* File: untitled
* Generated: Mon, 14 Apr 2025 13:27:12 +0800
*/
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#define MIDICHANNEL 0 // DEPRECATED
int32buffer AudioInputLeft, AudioInputRight, AudioOutputLeft, AudioOutputRight;
void xpatch_init2(uint32_t fwid);
extern "C" __attribute__ ((section(".boot"))) void xpatch_init(uint32_t fwid) {
xpatch_init2(fwid);
}
void PatchMidiInHandler(midi_device_t dev, uint8_t port, uint8_t status, uint8_t data1, uint8_t data2);
static void PropagateToSub(ParameterExchange_t* origin) {
ParameterExchange_t* pex = (ParameterExchange_t*) origin->finalvalue;
PExParameterChange(pex, origin->modvalue, 0xFFFFFFEE);
}
class rootc {
public:
static const uint16_t NPEXCH = 0;
ParameterExchange_t PExch[NPEXCH];
int32_t displayVector[3];
static const uint8_t NPRESETS = 0;
static const uint8_t NPRESET_ENTRIES = 0;
static const uint8_t NMODULATIONSOURCES = 0;
static const uint8_t NMODULATIONTARGETS = 0;
int32_t PExModulationPrevVal[1][NMODULATIONSOURCES];
/* Parameter instance indices */
/* Object classes */
class objectinstance_give__me__banana {
public: // v1
rootc *parent;
/* Object Local Code Tab */
int given = BANANA;
public: void Init(rootc *_parent) {
parent = _parent;
/* Object Init Code Tab */
}
public: void Dispose() {
}
public: void dsp() {
}
};
class objectinstance_define__banana {
public: // v1
rootc *parent;
/* Object Local Code Tab */
#define BANANA 1
public: void Init(rootc *_parent) {
parent = _parent;
/* Object Init Code Tab */
}
public: void Dispose() {
}
public: void dsp() {
}
};
/* Object instances */
objectinstance_give__me__banana objectinstance_give__me__banana_i;
objectinstance_define__banana objectinstance_define__banana_i;
/* Net latches */
static const uint32_t polyIndex = 0;
static int32_t* GetInitParams(void) {
static const int32_t p[NPEXCH] = {
};
return (int32_t*) &p[0];
}
static const int32_t* GetPresets(void) {
static const int32_t p[NPRESETS][NPRESET_ENTRIES][2] = {
};
return &p[0][0][0];
};
void ApplyPreset(uint8_t index) {
if (!index) {
int32_t* p = GetInitParams();
uint32_t i; for (i = 0; i < NPEXCH; i++) {
PExParameterChange(&PExch[i], p[i], 0xFFEF);
}
}
index--;
if (index < NPRESETS) {
PresetParamChange_t* pa = (PresetParamChange_t*) (GetPresets());
PresetParamChange_t* p = &pa[index * NPRESET_ENTRIES];
uint32_t i; for (i = 0; i < NPRESET_ENTRIES; i++) {
PresetParamChange_t* pp = &p[i];
if ((pp->pexIndex >= 0) && (pp->pexIndex < NPEXCH)) {
PExParameterChange(&PExch[pp->pexIndex], pp->value, 0xFFEF);
}
else break;
}
}
}
static PExModulationTarget_t* GetModulationTable(void) {
static const PExModulationTarget_t PExModulationSources[NMODULATIONSOURCES][NMODULATIONTARGETS] = {
};
return (PExModulationTarget_t*) &PExModulationSources[0][0];
};
/* Patch init */
void Init() {
uint32_t i, j;
const int32_t* p;
p = GetInitParams();
for (j = 0; j < NPEXCH; j++) {
PExch[j].value = p[j];
PExch[j].modvalue = p[j];
PExch[j].signals = 0;
PExch[j].pfunction = 0;
}
int32_t* pp = &PExModulationPrevVal[0][0];
for (j = 0; j < (1 * NMODULATIONSOURCES); j++) {
*pp = 0; pp++;
}
displayVector[0] = 0x446F7841;
displayVector[1] = 0;
displayVector[2] = 0;
objectinstance_give__me__banana_i.Init(this);
objectinstance_define__banana_i.Init(this);
uint32_t k; for (k = 0; k < NPEXCH; k++) {
if (PExch[k].pfunction) {
(PExch[k].pfunction)(&PExch[k]);
}
else {
PExch[k].finalvalue = PExch[k].value;
}
}
}
/* Patch dispose */
void Dispose() {
objectinstance_define__banana_i.Dispose();
objectinstance_give__me__banana_i.Dispose();
}
void __attribute__((optimize("-O2"))) clearBuffers(void) {
uint32_t u;
for(u=0; u < BUFSIZE; u++) {
AudioOutputLeft[u] = 0;
AudioOutputRight[u] = 0;
}
}
/* Patch k-rate */
void dsp(void) {
uint32_t i;
clearBuffers();
/* <nets> */
/* </nets> */
/* <zero> */
int32_t UNCONNECTED_OUTPUT;
static const int32_t UNCONNECTED_INPUT = 0;
static const int32buffer ZEROBUFFER = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int32buffer UNCONNECTED_OUTPUT_BUFFER;
/* </zero> */
/* <object calls> */
objectinstance_give__me__banana_i.dsp();
objectinstance_define__banana_i.dsp();
/* </object calls> */
/* <net latch copy> */
/* </net latch copy> */
}
void MidiInHandler(midi_device_t dev, uint8_t port, uint8_t status, uint8_t data1, uint8_t data2) {
}
};
static rootc root;
void PatchProcess(int32_t* inbuf, int32_t* outbuf) {
uint32_t i;
for (i = 0; i < BUFSIZE; i++) {
/* AudioInputMode == A_STEREO */
AudioInputLeft[i] = inbuf[(i<<1)] >> 4;
AudioInputRight[i] = inbuf[(i<<1) + 1] >> 4;
}
root.dsp();
for (i = 0; i < BUFSIZE; i++) {
/* AudioOutputMode == A_STEREO */
outbuf[(i<<1)] = __SSAT(AudioOutputLeft[i], 28) << 4;
outbuf[(i<<1) + 1] = __SSAT(AudioOutputRight[i], 28) << 4;
}
}
void ApplyPreset(uint8_t i) {
root.ApplyPreset(i);
}
void PatchMidiInHandler(midi_device_t dev, uint8_t port, uint8_t status, uint8_t data1, uint8_t data2) {
root.MidiInHandler(dev, port, status, data1, data2);
}
typedef void (*funcp_t) (void);
typedef funcp_t* funcpp_t;
extern funcp_t __ctor_array_start;
extern funcp_t __ctor_array_end;
extern funcp_t __dtor_array_start;
extern funcp_t __dtor_array_end;
void PatchDispose( ) {
root.Dispose();
{
funcpp_t fpp = &__dtor_array_start;
while (fpp < &__dtor_array_end) {
(*fpp)();
fpp++;
}
}
}
void xpatch_init2(uint32_t fwid) {
if (fwid != 0xEC73A8DA) {
// LogTextMessage("Patch firmware mismatch");
return;
}
extern uint32_t _pbss_start;
extern uint32_t _pbss_end;
volatile uint32_t* p;
for (p = &_pbss_start; p < &_pbss_end; p++) {
*p = 0;
}
{
funcpp_t fpp = &__ctor_array_start;
while (fpp < &__ctor_array_end) {
(*fpp)();
fpp++;
}
}
patchMeta.npresets = 0;
patchMeta.npreset_entries = 0;
patchMeta.pPresets = (PresetParamChange_t*) root.GetPresets();
patchMeta.pPExch = &root.PExch[0];
patchMeta.pDisplayVector = &root.displayVector[0];
patchMeta.numPEx = 0;
patchMeta.patchID = -2010510764;
extern char _sdram_dyn_start;
extern char _sdram_dyn_end;
sdram_init(&_sdram_dyn_start, &_sdram_dyn_end);
root.Init();
patchMeta.fptr_applyPreset = ApplyPreset;
patchMeta.fptr_patch_dispose = PatchDispose;
patchMeta.fptr_MidiInHandler = PatchMidiInHandler;
patchMeta.fptr_dsp_process = PatchProcess;
}
But if you put the define_banana object above give_me_banana, it compiles just fine.
Note that this is with the define in the Local Data tab. There is some confusion as to which object values are available where, for example I believe parameters and displays are not yet available in the Local Data, only in Init Code and later. The reason is, (or I might be wrong), that these values are inside the CPP class that is the object, and are declared in its constructor, or init() function or whatever. However, attributes are fine, since they literally do not exist as such in the CPP code, but are replaced with whatever value we select in the object.
#define LEMON(x,y) x ## y
LogTextMessage("%d", LEMON(attr_gir, affe));
I am not sure what you’re doing here, but maybe you’ll have a clue now that we know any attributes are just plain and dumb replaced by the Java code generator, so the C compiler will never even see them. According to this logic, attr_gir does not exist, neither does affe. Java will not replace either with any valid value and they will be passed unchanged to the C compiler, which will stand in front of a mutilated animal carcass and won’t know what the meaning of it is.