There is an sdk for developing plugins that is far more accessible than the in-built opcodes. I’d recommend starting there. Here is a repo that uses this framework and comes with a simple cmake integration. You can probably clone this, remove the deadweight, and then use it as a base for your opcodes.
p.s. The BUILD_PLUGIN macro is new in Csound7. When enabled, Csound will build all the internal plugin opcodes directly into the library binary. It’s not something you need to worry about unless you are commiting new opcodes directly to the core system.
My project is actually related to a class i’m doing right now.
I choose to translate a Csound Opcode to the pd/Max environment. this is basically a course where we learn C, so i need to stay with the C-language.
i try to understand the difference between
csdl.h and csoundCore.h
i tried to understand what the API doccumentation says about it, but it looks like both do the same thing:
to provide data structures that are necessary for building opcodes.
Also i’m little bit confused about the difference of ‘plugins’ and ‘opcodes’.
Like i understood it in the past ‘plugins’ are basically opcodes, that don’t belong to the core csound library and are dynamically loaded by csound.
besides ‘opcodes’ belongs to the core of csound.
so i guessed ‘csdl.h’ stands for ‘csound dynamic library’ and is especially for coding plugins and ‘csoundCore.h’ is for opcode coding.
and there had to be a difference in the past for coding plugins and opcodes, which is now solved by the BUILD_PLUGINS macro.
Plugin opcodes are loaded dynamically at run time. Internal opcodes are built in. I forgot that the plugin framework I linked can also be used for plugin developed in C. See this example here:
I’d recommend you go down this route as it’s the simplest way to go when it comes to developing plugins. This paper also has some nice info on how plugin opcodes work:
I compiled now a little GEN routine wrote based on the lorenz attractor and i’m happy that it compiled just with a little warning:
warning: incompatible function pointer types initializing 'int (*)(FGDATA *, FUNC *)' with an expression of type 'void (*)(void)' [-Wincompatible-function-pointer-types]
{ "lorenz", (void(*)(void))lorenztable},
EDIT: i found the problem!
While chcking the examples for csound 6 i did notice that the CSOUND pointer is not necessary anymore, but i did not notice that the order of FUNC and FGADTA as input to the GEN Routine did change…
Now i’m happy and my GEN Routine is working!
Now i’am at a point where i need help.
Status is like:
i noticed that the examples in the PDF above are probably deprecated
i checked the repo for examples from csound 6 for GEN routine codes and found “tanh” and other
i applied some changes
my code is compiling with gcc -shared -o lorenz-gen.dylib -I/Library/Frameworks/CsoundLib64.framework/Versions/6.0/Headers/ lorenz-gen.c
i load my dylib with --opcode-lib=lorenz-gen.dylib
but i get a segmentation fault in csound
this is my c-code:
#include "csdl.h"
#include "csoundCore.h"
/*
p3 = size
p4 = GEN routine
p5 = choose axis to write; 0 = x, 1 = y, 2 = z
p6 = min output
p7 = max output
p8 = stepsize
p9 = x start
p10 = y start
p11 = z start
f1 0 4096 "lorenz" "x" -1 1 5
*/
void scale_array(MYFLT *array, MYFLT min_out, MYFLT max_out, int32_t size)
{
printf("\nscale array start\n");
MYFLT min_in = array[0];
MYFLT max_in = array[0];
printf("\ninit min max\n");
for(int i = 1; i < size; i++){
if(array[i] < min_in)
min_in = array[i];
if(array[i] > max_in)
max_in = array[i];
}
printf("\nfound min max\n");
MYFLT old_value;
for(int i = 0; i < size; i++){
old_value = array[i];
array[i] = min_out + ( (old_value - min_in) / (max_in - min_in)) * (max_out - min_out);
}
}
static int32_t lorenztable(FUNC *ftp, FGDATA *ff)
{
printf("\ninit table\n");
/* the function table */
MYFLT *fp = ftp->ftable;
/* default arguments */
MYFLT sigma = 10;
MYFLT rho = 28;
MYFLT beta = 8.0/3.0;
MYFLT time = 0.001;
/* p-field arguments */
MYFLT x = ff->e.p[9];
MYFLT y = ff->e.p[10];
MYFLT z = ff->e.p[11];
MYFLT step_size = ff->e.p[8];
printf("\ninit args\n");
/* write raw data to ft */
for (int i = 0; i < (int32_t) ftp->flen; i++)
{
if (ff->e.p[5] == 0){
fp[i] = x;
} else if (ff->e.p[5] == 1){
fp[i] = y;
} else if (ff->e.p[5] == 2){
fp[i] = z;
}
for (int j = 0; j < step_size; j++){
x += (sigma * (y - z)) * time;
y += (x * (rho - z) - y) * time;
z += ((x * y) - (beta * z)) * time;
}
}
printf("\nwrite raw data\n");
/* scale data of ft */
scale_array(fp, ff->e.p[6], ff->e.p[7], (int32_t) ftp->flen);
printf("\nscale array\n");
return OK;
}
static NGFENS localfgens[] = {
{ "lorenz", lorenztable},
{ NULL, NULL}
};
FLINKAGE_BUILTIN(localfgens)
I’ve never tried creating any gen routines, but the majority of segfaults are from out of bounds access errors. I’d suggest stepping through the code with the debugger. That’ll pinpoint exactly where the problem lies.
I’m searching for a simple example of an opcode for audio array output and one for a k-array input.
i didn’t find the right data structures for this.
also wondering if it is working of opcode development with flexible audio array output, e.g. i have an i-argument which describes the size of the audio array output.
I’ve never build an opcode that outputs an audio rate array, but I have written some that output multiple k-arrays. In my case the k-arrays were variable length so I also output a size var that I can use to safeguard against out of bounds errors.
Thanks again Rory!
I’m now working through the Floss-Example, which is also quiet useful but in still have some questions.
Another important thing to consider is to support the –sample-accurate mode introduced in Csound 6. For this we will need to add code to start processing at an offset (when this is given), and finish early (if that is required). The opcode will then lookup these two variables (called offset and early ) that are passed to it from the container instrument, and act to ensure these are taken into account.
i have read this and i’m wondering what this sample accurate mode is.
Is this, to make all audio processes in Csound synchronous? so that the vector output ist always at the same point of time for all opcodes?
Yeah, I’m not sure many people are aware of this flag. It allows instruments to start and stop somewhere within a k-boundary. Without this flag instruments can only ever begin or end at the start of a k-cycle. I’m not sure how well-used it is. With ksmps set to 32 that gives a maximum offset of less than a millisecond. I guess if you need to use large ksmps for some reason, then it’s a good flag to know about.
So these both values are updated every k-cycle?
And when the -sample-accurate flag is not in use, csound gives back 0 for both?
and only one of these is not zero?
uint32_t offset = p->h.insdshead->ksmps_offset;
uint32_t early = p->h.insdshead->ksmps_no_end;