Build_plugins

Hello everyone,

i’m getting started in plugin development in csound.
So i try to understand some opcode c code.

The biggest part is understanding the Csound environment.
My first problem is understanding this:

#ifdef BUILD_PLUGINS
#include "csdl.h"
#else
#include "csoundCore.h"
#endif

i was searching about the BUILD_PLUGINS (macro?), but i don’t find the ressources for this.
What is this? Is this a macro for cmake?

I’m quiet a noob i c development but i try to get my head around this.
Greetings,
Philipp

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.

Thanks rory for your reply!

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.

Ah Ok. I did something similar when I was learning C. I think it was the balance opcode I translated. Where are you taking your class?

I study electronic composition at Folkwang, Essen, Germany.

I hope it’s ok to disturb you with some further questions down the road!

Natürlich :slight_smile:

1 Like

I’m already a little confused.

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:

The PDF is a really great resource!
I’m working my way through it and coming back with questions that are still open! :slight_smile:

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)

this is how i call the gen routine inside csound

f1 0 1024 "lorenz" 0 -1 1 1 0.001 0 0

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.

diskin2 outputs an array of a-rate signals, you might look there.

You can output whatever size array you want and use lenarray to query its size?

you can access array input like this:

csnd::Vector<MYFLT>& inputArgs = args.myfltvec_data(ARG_NUM);

To define an opcode with an array out, and array in I think you can do this:

csnd::plugin<MyOpcode>(csound, "myOpcode", "a[]", "k[]", csnd::thread::k);

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 Rory!

Since i don’t know a thing about C++ and i use the C-API do you also have experience with this?

Sorry, I keep forgetting that! I think the OENTRY stuff would be similar, i.e:

{ "myPlug", sizeof(MY_PlUG), 0, 2, "a[]", "k[]", NULL, (SUBR) my_plug_process }

I’m not 100% sure about accessing the input/output array in C. I think you’ll need to check the opcode source to find an example.

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;

because i don’t get this completely

       /* sample-accurate mode mechanism */
      if(offset)
        memset(aout, '', offset*sizeof(MYFLT));
      if(early) {
        n -= early;
        memset(&aout[n], '', early*sizeof(MYFLT));
      }

if both would be not zero, then the whole vector would be filled with ‘nothing’.