Wusik 4000 Module SDK Docs Ready

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS
Wusik 8008

Post

Now I'm consolidating the whole set of classes into a single W4kMod class, and removing the W in front of everything. So instead of WMouseEvent it will be W4kMod::MouseEvent... and so on... much cleaner indeed. Thanks again!

I just don't get, yet, the different from struct to a class, really don't get if I could use either and why, but maybe I'm just tired...

Post

Done, everything is inside a single class now. I'm also trying to organize things a bit better, not sure if I'm doing a good job or not. :oops:

I also updated the Gain Effect and the Effect->Simple Low Pass Filter - the other modules I will update once I'm finished changing things up.

That's the main reason I wanted to do this now, before I have many more modules. :hihi:

Post


Post

Ok, final edit for today. I wait for some remarks so I know I'm heading into the right direction. :cool:

I renamed two classes: Parameters to Parameter and Options to Option, as this makes more sense. I also updated the Docs to reflect the latest changes. And also updated the same files as above: Gain Effect example and Effects->Low Pass Filter

Thanks again for all the help! :hyper:

Post

You said something about ugly-macros, do you mind pointing me out which ones and how I could improve this up? Please?

Post

WilliamK wrote:One part that I still don't like is that since I'm doing 32 and 64 bit versions, I need to worry about things like 1.0 and 1.0f.
William,

When you say "doing 32 and 64 bit versions" I assume you mean versions that will execute in 32-bit or 64-bit address spaces (and where 64-bit versions cannot be run under a 32-bit OS). That has nothing to do with whether the data being manipulated in the processing is single or double prescision floating point. 32-bit processes can do computations with double precision (i.e., 64-bit) floating point, and vice versa.

Or am I missing your point in that comment?

Post

I just use a typedef:

Code: Select all

typedef float fT;

fT var = fT(1.0);
The compiler should do the optimization for you, although it does depend upon the compiler.

With constants you have to use either static const, or the new c++11 feature "constexpr".

Like "enum class", "constexpr" isn't widely supported yet. :cry:

While static const sucks, it does actually work just fine except in cases where the value could have been inlined by the compiler during optimization.

For example:

Code: Select all

template <class V, class F>
V lerp(V x, V y, F z) 
{
 return x + (y - x) * z;
}

template <class V, class F>
V lerpparabolic(V x, V y, F z) 
{
 return lerp(x, y, z*z * (F(2.0) - z*z);
 // or...
 static const F two = F(2.0);
 return lerp(x, y, z*z * (two - z*z);
}
By using a static const for 2.0 the compiler may be forced to load it from memory whereas by in-lining the value it may be able to avoid that extra operation.

On the other hand if the compiler does something insane like using the double in place of converting to "F" at compile time, depending upon F<>double you may end up in a less optimal situation.

Const-expr to the rescue!... oh wait. Uh, I'm sure he'll save us in the next few years.
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

WilliamK wrote:You said something about ugly-macros, do you mind pointing me out which ones and how I could improve this up? Please?
All the macro functions need to be in their own header called "wusik4k_macros.h" or similar.

They shouldn't be by default.

For example the macros you wrote to make the code shorter when you access parameters...

This is not a healthy way to do it as it hides what is actually going on behind the macro. We don't know which object is being accessed or which member... all we see is wPar(cutoff) and we have to guess or look at the macro definition.

There are ways to design the interface itself to simplify parameter access.

For example, get_parameter(cutoff) <-- what is wrong with this? get_modulated(voice, cutoff), is_modulated(cutoff) and so on.

In the very least why not use inline wrapper functions rather than rely upon the macro? The problem with macros is they can not be easily debugged - it isn't possible to step through code generated by a macro.
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

Also the disadvantage of using a class as a namespace is that a class definition can't be divided into different files unless you inline-include.

If you put each object in a namespace it can go into it's own header and this can help to reduce the clutter. So long as you're not using forward-declarations to reduce the amount of code that is recompiled when you make a change though I suppose it is mostly aesthetic.

Some of the macro functions would be better as member functions in the object they operate upon.

For example:

Code: Select all

#define MinMax(value,enumParameter) ((value*(wPar(enumParameter)->modMax - wPar(enumParameter)->modMin))+wPar(enumParameter)->modMin)*wPar(enumParameter)->modAmount

Why not make this a function as a member of "Parameter" ?

Parameter
{
 minmax(v)  
 {
  return (v * (modMax - modMix) + modMin) * modAmount;
 }
}
Although I don't understand why you're passing in a value when the value is a member of the object... (Ah! I see now. This function should be called "translate(value)" as a member of "Parameter". It's purpose is to take an outside normalized parameter (0 ... 1) and translate/scale it to the parameter's min/max range. Also would be great if you'd add a comment like that above the function :))

Then minmax reduces to: parameter->minmax();

With an inline wrapper-function:

Code: Select all

minmax(int parameter_index)
{
 return parameter[parameter_index]->minmax();
}
then you can still use your function-call format like with the macro, only it's possible to step through the code even though the compiler can inline it.

Another advantage is you can assert the parameters are valid:

Code: Select all

minmax(int parameter_index)
{
 assert(parameter_index >= 0);
 assert(parameter_index < num_parameters);
 return parameter[parameter_index]->minmax();
}
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

Thanks again, I will read all this again later today and see what I can do about. :cool:

Post

aciddose wrote:I just use a typedef:

Code: Select all

typedef float fT;

fT var = fT(1.0);
For now I will use this, good idea. :cool: It also simplifies my code.

Post

A lot of people prefer the c++ casts syntax but I prefer the constructor syntax in most cases.

Keep in mind depending upon the type of cast you're doing, it may make more sense to use a cast that checks for wrapping errors. For example when converting between two int types such as "char(int)" you should use a c++ style cast template that checks for errors.

For the simple float<>float or float<>double cases the constructor cast works fine.

I find it most useful using these sort of interpolation (or other) templates because you can also do lerp(vector_1, vector_2, float_fraction), or lerp(matrix_1, matrix_2, vector_fraction) or whatever other sort of crazy implementation you like.

This is where operator overloading becomes extremely useful - in combination with constructor casts and templates.
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

I'm not sure I understand this right now, but I want to, do you mind pointing me to some tutorials? Or how should I search this?

Also, a friend told me to avoid too long lines in the code, so I will also take a look into this. :cool:

I'm also trying to figure out a better way to handle some other things from your suggestions. Heck, I'm trying here, really want to make this SDK very easy to use so more and more people join this.

Post

Now, please, help me out here. I was wondering on the FLAG system. Right now I use a single int32 variable for the flag. And the bitOperation to check, set, clear flags. BUT, I'm wondering on using a structure for this instead, but I wonder if this is safe. Here's an example:

Code: Select all

struct Flag
	{
		Flag() { flag = 0; };
		inline void set(const int32 flagEnum) { flag |= bitToMask(flagEnum); };
		static inline uint32 bitToMask(const int32 flagEnum) { return (uint32) 1 << (flagEnum & 31); };
		int32 flag;
	};
So, if I declare the Flag flag instead of int32 flag, is this safe to go from the Dynamic Library to the Host and back? I have no clue on this. I will test, it should be safe, since the structure is carefully declared, so I guess it is safe. I will run some tests tomorrow...

Thanks again! :hug:

Post

Here's a more completed code. Please, can you guys give me some pointers?

Code: Select all

	struct Flag
	{
		Flag() { value = 0; };
		inline void set(const int32 flagEnum) { value |= bitToMask(flagEnum); };
		inline void clear(const int32 flagEnum) { value &= ~bitToMask(flagEnum); }
		inline bool isSet(const int32 flagEnum) { return ((value & bitToMask(flagEnum)) > 0); }
		inline bool isClear(const int32 flagEnum) { return ((value & bitToMask(flagEnum)) == 0); }
		static inline uint32 bitToMask(const int32 flagEnum) { return (uint32) 1 << (flagEnum & 31); };
		int32 value;
	};
This way I can setup like this:

Flag flags

and just go like this:

flags.set(kSomeFlagEnum)

Good boy or ugly boy, now? :hihi:

Post Reply

Return to “DSP and Plugin Development”