The DLL Component – Getting Started, Part 2: Creating a basic DLL

The DLL Component – Getting Started, Part 2: Creating a basic DLL

No Comments

In this article I’ll show you the creation of a basic Flowstone DLL that implements a few simple functions that hopefully will demonstrate most things you need to know about creating a Flowstone DLL (Note I won’t be explaining C++ in any real depth because that is beyond the scope of this tutorial).

I am also assuming you have followed the previous article and have a Netbeans project already set up and ready to go, although you should be able to follow this tutorial in any IDE.

The function

First lets take a look at the definition of a function that Flowstone is expecting.

extern "C" __declspec(dllexport) void myFunction( int nParams, int* pIn, int* pOut )
{
}

You should recognize that function as the one in your project. All functions in your DLL have to be written exactly like that. We can change the name of the function to anything we like though.

If you want to understand what ” extern “C” __declspec(dllexport) ” really means then just do a Google search on that, in simple terms it is allowing us to export the function to be called outside the DLL and with the C calling convention, which prevents Name mangling.

The rest of the function is simple enough. “void” is the return type, which means it literally returns nothing. “myFunction” is the function name, this is the only part of the declaration that can be changed. “nParams” is an integer value giving the number of parameters. “pIn” is an int Pointer to an int array containing the memory addresses for all our inputs to the DLL component; and of course “pOut” is pointing to an int array holding all our outputs.

Now I will show you a few basic algorithms and how they can be achieved using the DLL component. In the next article (part 3) I will show you how to set everything up and call the functions from within Flowstone.

 Writing a reverse int array function

OK so let’s get straight on with writing our first function. This function will simply take an int array reverse it and then send it to the output. Simple, but will shed some light on a couple of things.

extern "C" __declspec(dllexport) void reverseIntArray( int nParams, int* pIn, int* pOut )
{
    int* intArrayIn = GETINTARRAY(pIn[0]); //Input 0 is an int array
    int size = GETARRAYSIZE(pIn[0]);
    
     //Here we create a new output array if needed 
     if(pOut[0]) //check if pOut[0] exists (not NULL)
     {
         if(size != GETARRAYSIZE(pOut[0])) //Check if size of input/output arrays are the same
         {
            DELETEINTARRAY(pOut[0]); //Delete old one
            NEWINTARRAY(pOut[0], size); //Create with new size
         }
     }else{
        NEWINTARRAY(pOut[0], size); //No output array so create one.
     }
    
     int* intArrayOut = GETINTARRAY(pOut[0]); //Get a pointer to the output array 

     int j=0;
     for(int i=size-1; i >= 0; i--) //Loop backwards starting from "size"
     {
         intArrayOut[j] = intArrayIn[i];
         j++; //j increments forwards as i goes backwards
     }
}

So this function is showing us the usage of some of those funky defines! Firstly we are using GETINTARRAY to get an int array from the first input, then we use GETARRAYSIZE to get its size.

Now we check if an output array needs to be created.  If pOut[0] is NULL we create a new array for the output, if it’s not NULL we check the input array size against the output array size, if they do not match(meaning input array has changed size since last time the  function was called) then we delete the old array and create a new one so the sizes match.

Now we use GETINTARRAY to get a pointer to the output array.
The rest of the code should be pretty self explanatory to anyone with basic programming knowledge.

So what this simple function is showing us is that we need to use those defines to delete and create new arrays ect. When we pass an array in Flowstone to the DLL component this array is copied (behind the scenes) which prevent us from modifying the original. We then are passed the address of this copied array. We then have to get this address into a form we can use, hence the GETINTARRAY function which gives us an int pointer which points to the beginning of the array. Once we have the pointer we can treat it like any array in C++.

In Flowstone when you assign an input on the DLL component, a corresponding output is created. So if pIn[0] is an int array then so is pOut[0], only we have to manually create the output array ourselves and then copy our data into that, as shown in the example above.

Writing a Mem section function

This function is essentially just an array section function(output a section of an array) but for Mem data type. The DLL component doesn’t support the Mem data type, but we shall not let that stop us! We can pass a Mem by passing its address and casting that to a float pointer, so here is the function….

extern "C" __declspec(dllexport) void memSection( int nParams, int* pIn, int* pOut )
{
    //Main Mem
    float* mem = (float*)pIn[0]; //Simply cast the address to a float pointer
    int size = pIn[1]; //Passing the size on the second input
    
    //Mem Section
    float* memSection = (float*)pIn[2]; //Mem to copy section too
    int startIndex = pIn[3]; //Where section will start from
    int sectionSize = pIn[4];
    
     for(int i=0; i < sectionSize; i++)
     {
         if(i+startIndex < size) //Make sure we don't read beyond the size of the Mem
         {
             memSection[i] = mem[i+startIndex];
             
         }
     } 
     
}

Here we are passing two mems, “mem” and “memSection”. “mem” could be a wave file, “memSection” is a mem that we need to create in Flowstone using the mem create component. This will be shown in the next article which focuses purely on the Flowstone side of things and will complete all these functions. The main purpose of this code is to show you a simple but rather useful example of how to read and write to the mem data type in a Flowstone DLL.

Writing a basic over drive function

This is just a very basic overdrive algorithm to show how to write effects for streams (audio).
Here is the code…

extern "C" __declspec(dllexport) void overDrive( int nParams, int* pIn, int* pOut )
{
    
    float* audio = GETFRAME(pIn[0]); //A frame is just a float array
    int frameSize = GETFRAMESIZE(pIn[0]);
    
    float gain = GETFLOAT(pIn[1]);
    float in=0;
    
    for(int i =0; i < frameSize; i++)
    {
        in = audio[i]*gain; //Apply gain
        
        audio[i] = in/((in*in*0.25)+1); //Saturation   
    }
}

Again this should be pretty simple stuff to anyone with a bit of programming experience. Audio is passed to the function in frames. Within Flowstone or an exe the size of the frame is determined by the buffer size of your audio driver (ASIO, Direct sound ect). Within a VST host the frame size is determined by the host and can differ to that of the audio driver. The frame size can even change on the fly.  You can never rely on a fixed sized frame, so always use GETFRAMESIZE and never try hard coding the frame size.

The other interesting thing about frames is that they are treated a bit differently to the other data types. You may have noticed in the code that there are no references to any pOuts (outputs), hence nothing is explicitly passed to an output. We perform the algorithm on the input frame and then assign that back to the very same input frame, then as if by magic it is passed to the output. This assignment back to the input frame does not actually modify the original input frame(as passed from Flowstone), because in the DLL the input frame is the output frame! You can test this by changing pIn[0] for pOut[0] in the code and it will work exactly the same. So what is going on here? Flowstone is copying the original contents of the input frame to another frame , this copy is then assigned to both the input and output (pIn[0] and pOut[0]), hence we can read and write from either.

Building the DLL

Now we have three simple functions to call from inside Flowstone, the next step is to build the DLL and then we can open Flowstone and get started on creating the schematic.

To build the DLL simply right click the project name and click “Build” then if successful you will see this message in the output window “BUILD SUCCESSFUL (total time: xxs)”,  and that is it!

In the next article I will show how to set the DLL component up in Flowstone to call our functions.

0 0 0 0 0
Exo

About the author:

I am the founder of Flowstone Guru. I have been using Flowstone since the early days when it was Synthmaker, talking 7-8 years now! I have created lots of things in Flowstone and have a wealth of experience with the software, some of my work you will find on this site in the Downloads section. I'm passionate about programming (Flowstone, Java ,C++,Ruby) there is nothing I like to do more. I just love a challenge and enjoy pushing myself and the tools I use to there limits!

Tags:
Add Comment Register



Leave a comment

You must be logged in to post a comment.

Back to Top