initialize variable number signal outlets to 0.0

due_friday's icon

Hi,

I am trying to code an msp external with variable number of outlets (i.e. the number of outlets is unknown to me while writing my code)

To make my code more efficient, I determined that only 2 outlets compute a signal at any given time - this means, the rest output a signal of 0.0

I am able to determine these two signal outlets on a per-sample basis. This means, on every sample I recompute which of the many outlets are the two that need to do any dsp - the rest merely output a signal of 0.0

Currently I do this by having a nested loop, naïvely writing a value of 0.0 to every outlet (on every sample), then overwriting the two computed values to the respective outlets (previously determined as described above).

My question is: Can I avoid the nested loop?

I.e. is there a way to initialize my t_float** to 0.0 to have *all* outlets silenced and then overwrite my values to each respective outlet on each sample as needed?

I tried using memset, for example, but for some odd reason, computing the sample values in the succeeding loop thereafter does not overwrite anything and all my signals are merely silenced (constant 0.0 value).

I would LOVE to look that objects like [gate~] as it seems very efficient, but since its outlet control is also control rate based (not audio rate based, like the object I am writing), it also might not help very much in my case.

I  would embrace any help if anyone has experience with this.

P.S.: I tried stating this question before here, but I am afraid my wording was very confusing. Please let me know if you do not quite understand my problem. Need help! Thanks!:)

Luigi Castelli's icon

Hi there,

DISCLAIMER: you have been pretty clear in your explanation and I think I understood the problem you're having, however keep in mind that without seeing actual code I still have to make a more or less educated guess...

Let's begin: you need to know that the default state of any MSP external is to automatically perform some optimization under the hood. This particular optimization entails having the input and output vectors share their memory and therefore operate "in-place". If I understand you correctly, in order to speed up your external and avoid the nested loop, you want to silence all output vectors first, and then only overwrite to each respective outlet those sample that are not going to be zero in the final output. Let me tell you that your thinking is definitely in the right direction, however because of that automatic optimization I mentioned above, you won't see the results you expect.

You need to add this line of code in the new method of your external:x->obj.z_misc |= Z_NO_INPLACE;

This will prevent the input and output vectors to operate "in-place" and therefore they won't share their memory. At this point your optimization strategy should work and you will be able to only write to the two active outlets. I think you'll also manage to avoid the nested loop in this scenario.

However I still recommend you to check if you obtain a significant speed gain, because in a way you're making your algorithm slightly slower (by telling MSP not to operate "in-place"), to make it work faster.

I hope I have been clear enough in my explanation.
Let me know if this is not the case...

Cheers

- Luigi

Emmanuel Jourdan's icon

FWIW, gate~ can be controlled at sample rate:

Max Patch
Copy patch and select New From Clipboard in Max.

due_friday's icon

@Luigi! It worked!

Seriously, you have been clear enough in your explanation and it did the trick! I now set all outlets to constant 0.0 using memset and then only insert the computed samples in the respective outlets at each sample point.

Since you were wondering, my informal test using 6 of those objects with 20, 12, 10, 6, 5 and 4 outlets in one patch made the CPU utilization drop from 9% to 7% percent with the above change (there are ~50 sends~ and receives~ among a few other objects in the patch as well). While its not a real test, it did indicate a small performance increase. Yes!

Thanks for the tip!

Oh, could you point me to where I can read up on these advanced MSP topics? Or is this something one should find out by reading through the header files?

Thanks again!

@Emmanuel: But, will Cycling '74 release the source code for gate~?? (Or did they and I simply cannot find it?)

Luigi Castelli's icon

@Due_friday

I am glad it worked.
I would consider the performance improvement you see fairly significant.

I think I read about it in one of the old SDK manuals.
Also, it popped up a bunch of times in the Max forum through the years.

Ah - found it - from the Max 4.5 SDK manual:

Special Bits in the t_pxobject Header

There are three bits you can set in the t_pxobject or t_pxbox header that affect how your object is treated when MSP builds the DSP call chain. The explanation of these settings will make more sense once you have read more about the dsp and perform methods, but they are explained here because you need to set them in your new instance routine. Both t_pxobject and t_pxbox contain a field called z_misc; by default it is 0 meaning that all of the following settings are disabled.

#define Z_NO_INPLACE 1

If you set this bit in z_misc, the compiler will guarantee that all the signal vectors passed to your object will be unique. It is common that one or more of the output vectors your object will use in its perform method will be the same as one or more of its input vectors. Some objects are unable to handle this restriction; typically, this occurs when an object has pairs of inputs and outputs and writes an entire output on the basis of a single input before moving on to another input- output pair.

#define Z_PUT_LAST 2

If you set this bit in z_misc, the compiler puts your object as far back as possible on the DSP call chain. This is useful in two situations. First, your object's dsp routine might require that another object's dsp routine is called first in order to work properly. Second, if your object wants another object's perform routine to run before its own perform routine. For example, to minimize delay times, a delay line reading object probably wants the delay line writing object to run first. However, setting this flag does not guarantee any particular ordering result.

#define Z_PUT_FIRST 4

If you set this bit in z_misc, the compiler puts your object as close to the beginning of the DSP call chain as possible. This setting is not currently used by any standard MSP object."

Now there are even more flags you can specify.
Of course you can check them all out in z_dsp.h.

Cheers.

- Luigi