catastrophic breakdown of gen codebox
Diemo Schwarz
2月 14 2023 | 10:17 午後
...and just when I had my gen algorithm working, after cleaning out the debug code, nothing works anymore. No error message, but the gen code in the codebox seems to be completely ignored (as seen from the generated c-code: // the main sample loop;
while ((__n--)) {
const t_sample in1 = (*(__in1++));
const t_sample in2 = (*(__in2++));
t_sample out1 = ((int)0);
t_sample out2 = ((int)0);
// assign results to output buffer;
(*(__out1++)) = out1;
(*(__out2++)) = out2;
};
what's going on here?
Joshua Kit Clayton
2月 15 2023 | 12:50 午前
Hi Diemo,
Interesting bug. You can't write to globally defined history objects within subroutines. If you remove your writing to those, your code gets generated. Would be nicer for gen to detect this and report an error but that's my quick take on "what's going on here". I would recommend passing in the globals and returning tuples to set these global variables if possible.
Hope that helps. If I'm overlooking something more subtle in my quick take, please let me know.
-Joshua
Diemo Schwarz
2月 15 2023 | 8:36 午前
Hi Joshua, thanks for looking into this bug. It would be good if restrictions like these were documented somewhere.
Unfortunately, for me, removing the write to a History variable in functions doesn't help, neither passing in the History variable as a parameter, neither removing the History declaration alltogether, neither localising the names of all function parameters.
The remaining difference with the working debug version is that the latter used external buffers (bound to a mubu for having a nice table view ;-) instead of internal Data arrays.
Here's the code that still doesn't compile, nor give an error:
get_index_from_id (id, id_to_index0)
{
return peek(id_to_index0, id);
}
get_unused_index (id, index_to_id00)
{
// find first free index
for (index = 0; index < dim(index_to_id00); index += 1)
{
if (peek(index_to_id00, index) < 0)
break; // free slot found (-1)
}
return index;
}
add_id (id, count1, index_to_id1, id_to_index1)
{
index = get_unused_index(id, index_to_id1);
// poke(v, i) writes value v at position i (!)
poke(index_to_id1, id, index);
poke(id_to_index1, index, id);
return index, count1 + (index < index_to_id1.dim);
}
remove_id (id, index, count2, index_to_id2, id_to_index2)
{ // clear array slots: set to -1
poke(index_to_id2, -1, index);
poke(id_to_index2, -1, id);
return count2 - 1;
}
Data index_to_id (32);
Data id_to_index (32);
History clear(1);
//History count(0);
count = 0;
if (clear)
{
// do this at init, or when clear flag is set from outside
for (i = 0; i < dim(index_to_id); i += 1)
{ // set all arrays to -1 meaning empty slot
poke(index_to_id, -1, i);
poke(id_to_index, -1, i);
}
count = 0;
clear = 0;
}
// input:
// in1: integer ID
// in2: 'activation' value >0 is on
id = in1;
vel = in2;
// check if id is already known
index = get_index_from_id(id, id_to_index);
if (index < 0)
{ // no: find first unused index and add id
index, count = add_id(id, count, index_to_id, id_to_index);
}
// check if id switches off
if (vel == 0)
{ // clear slots
count = remove_id(id, index, count, index_to_id, id_to_index);
}
out1 = index;
out2 = count;
Joshua Kit Clayton
2月 16 2023 | 8:39 午後
Hi Diemo,
Sorry that some of these advanced use cases do not have clear error messages (I'm only actually empirically determining that these are limitations). However, in your latest example, I was able to make it compile to something sensical by changing the following code's usage of index_to_id1.dim to dim(index_to_id1):
add_id (id, count1, index_to_id1, id_to_index1)
{
index = get_unused_index(id, index_to_id1);
// poke(v, i) writes value v at position i (!)
poke(index_to_id1, id, index);
poke(id_to_index1, index, id);
return index, count1 + (index < index_to_id1.dim);
}
Diemo Schwarz
2月 16 2023 | 10:09 午後
Ah, thanks, that unblocks the situation! I only came across that oo-ish syntax in some tutorials, like with buf.poke(v, i) instead of poke(buf, v, i). I have scoured the documentation at long and large, and were never able to find a true reference documentation giving the formal syntax of the genexpr language.
For the record, the global History variable really also needs to be passed into the function (as count1 above) to be available. Wouldn't it be nice if globals were seen inside of functions?
Cheers!
Diemo Schwarz
2月 22 2023 | 7:18 午後
codebox keeps on giving!
I'm trying to generalise my data arrays to more than one channel (to store several instances of my data), given as a parameter (default 1).
Is this way to define the number of channels actually possible?:
Param size;
Data mydata(21, size);
If so, there's another problem in the gen compiler, the below code produces an assertion:
gen_domain: dsp.gen: [string "gen2.dsp.Model"]:1455: assertion failed!
gen: failed to compile patcher
get_index_from_id (id, id_to_index, inst)
{
return peek(id_to_index, id, inst);
}
get_unused_index (id, index_to_id, inst)
{
// find first free index
for (index = 0; index < dim(index_to_id); index += 1)
{
if (peek(index_to_id, index, inst) < 0)
break; // free slot found (-1)
}
return index;
}
add_id (id, count, index_to_id, id_to_index, inst)
{
index = get_unused_index(id, index_to_id, inst);
// poke(v, i) writes value v at position i (!)
poke(index_to_id, id, index, inst);
poke(id_to_index, index, id, inst);
addit = (index < dim(index_to_id));
poke(count, peek(count, inst) + addit, inst);
return index;
}
remove_id (id, index, count, index_to_id, id_to_index, inst)
{ // clear array slots: set to -1
poke(index_to_id, -1, index, inst);
poke(id_to_index, -1, id, inst);
poke(count, peek(count, inst) - 1, inst);
}
Param instances; // number of independent alive lists
Data index_to_id (32, instances);
Data id_to_index (32, instances);
Data count (instances);
History clear(1);
if (clear)
{
// do this at init, or when clear flag is set from outside
n = dim(index_to_id);
m = channels(index_to_id);
for (k = 0; k < m; k += 1)
{
for (i = 0; i < n; i += 1)
{ // set all arrays to -1 meaning empty slot
poke(index_to_id, -1, i, k);
poke(id_to_index, -1, i, k);
}
poke(count, 0, k);
}
clear = 0;
}
// input:
// in1: integer ID
// in2: 'activation' value (velocity or touch phase)
id = in1;
act = in2;
// in3 is flag how to interpret activataion:
// - 0: as velocity,
// - 1: as ROLI touch phase (0 - begin, 1 - middle, 2 - end)
on = in3 == 0 ? act > 0 : act != 2;
inst = in4; // current instance number = channel
// check if id is already known
index = get_index_from_id(id, id_to_index, inst);
if (index < 0)
{ // no: find first unused index and add id
index = add_id(id, count, index_to_id, id_to_index, inst);
}
// check if id switches off
if (!on)
{ // clear slots
remove_id(id, index, count, index_to_id, id_to_index, inst);
}
out1 = index;
out2 = peek(count, inst);
Diemo Schwarz
2月 25 2023 | 12:33 午後
Futzing around with simpler code I stumbled on the solution: all functions MUST have a return statement. It would be helpful if gen's error message said so instead of "[string "gen2.dsp.Model"]:1455: assertion failed!"
genexpr is supposed to be a programming language.
Can we please have a reference documentation of this language giving its syntax and semantics?