Msp: Do you need to use + for combining signals?

Sym's icon

Hey, I'm currently working on a project with additive synthesis.

I would like to know if i need to sum all the signal up with + objects first or if i can send them all into one * object.

I used to do it in one * object but today I was told that this is not the proper way on doing it.

Anyone got an opinion?

spectro's icon

AFAIK MSP signals are summed when connected to the same inlet. So it would be ok to do what you want, ie send all the signals to one [*~] object. With that said, it may be possible to do what you want with oscbank~ or ioscbank~

brendan mccloskey's icon

....with the proviso that your output should not exceed 1., natch'

Brendan

wiccy's icon

Yes, that is posible, but remember is the whole signal should not exceed 1. So let say you have 3 osc in with full amplitude. After summing up you should / 3. Or it will distort pretty badly

spectro's icon

Just to be clear on this: It *is* true that a signal to the output (to a physical output, another application etc) should not exceed 1. However, It is not true that the signal amplitude should not exceed 1 within MSP as it will happily handle values > 1. internally.

jaeho's icon

What others said are true,
but occassionally you need to use [+~] for "efficiency".

When two or more signals go to the same inlet, MSP automatically creates [+~] internally, which means that the number of total function calls increase and the CPU works more.

For example, the patch 1 and the patch 2 are doing same thing, but there are 11 function calls in the patch 1 while 9 function calls in the patch 2. (You can see this in the DSP Status(Max 5) or Audio Status(Max 6) window.)

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

patch 1:

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

patch 2:

Luke Woodbury's icon

And BTW [*~ 0.5] is more efficient than [/~ 2]

Sym's icon

Thank you guys! Many intresting points. Especially what Luke said.

Peter Castine's icon

What Luke said may be interesting, but it's wrong.

[*~] and [/~] are equally efficient with non-signal values.

Yes, it is well known that division is computationally more expensive than multiplication, *but* the [/~] object also knows this. So, when it is dealing with a non-signal value, it precalculates the inverse and multiplies with that during signal processing. IOW, [/~ 2] and [*~ 0.5] do exactly the same thing.

It is only with signals that [/~] becomes more expensive. Since the signal is constantly changing (at least potentially), a division has to be performed on every sample.

This (as well as when a signal needs to be constrained to unit range) are all covered in the documentation.

[EDIT: corrected typo]

Roman Thilenius's icon

"And BTW [*~ 0.5] is more efficient than [/~ 2]"

"it's wrong." - "It is only with signals that [/~] becomes more expensive."

can [/~] take something else but signals? or is there a difference between [/~] and [/~]?

Roman Thilenius's icon

oh, ok, i see now. (too late i guess). you mean [/~ 2] with no signal as divisor only has to compute the inverse once every vector.

question: is the inversion thing new or how did max 3, 4, and 5 handle this?

Luke Woodbury's icon

I blame the documentation:

"To control the level of a signal you simply multiply each sample by a scaling factor. For example, to halve the amplitude of a signal you simply multiply it by 0.5. (Although it would be mathematically equivalent to divide the amplitude of the signal by 2, multiplication is a more efficient computation procedure than division."

Maybe it is good practice anyway?

Thijs Koerselman's icon

What Peter was trying to say I think is that if you use [/~] with a constant value (either as argument or by sending a float or int to the right inlet) you can be sure that every sample of the audio vector coming into the left inlet needs the same calculation. Then you might as well process the values by doing the inverse multiplication instead. The object is clever enough to take this into account so performance wise there is no difference between the 2 objects in situations like that.

As soon as you connect an audio signal to the right inlet ALSO it can't know this because for every vector in its left inlet it is getting a devision value in its right inlet on a per sample basis. There is no way to know if the devision is going to be constant for the whole vector before hand.

You could calculate the inverse for the whole right side input vector, and use that for multiplication, but then you're probably not gaining anything performance wise.

So yeah in that sense there is a difference between [/~] and [/~], depending on how you use them.

Thijs Koerselman's icon

Ah I was too slow :)

LiamCormacGould's icon

very interesting info guys, I love these forums :)

Roman Thilenius's icon

"Maybe it is good practice anyway?"

the application here was mixing an uncertain number of signals together, so i would
say it could make sense to use a signal anyway: if the oscillator changes from 6 to 7
partials, the gain scaling factor has to be changed - and you will probably want to
interpolate that, so you will be using a signal.

and last but not least it is always only one [/~ ] object, no matter if you are summing
7 or 700 signals.

if i had to build that i would 110ly end up with something like that:

"7." - [!/ 1.] - [line~] - [*~ 0.]

and the dicussion about [/~ 0. ] theories is over.

-110

Peter Castine's icon

oh, ok, i see now. (too late i guess). you mean [/~ 2] with no signal as divisor only has to compute the inverse once every vector.

Not even that often.

You know, it's amazing what you can learn by reading the documentation. Here, straight from div~.maxhelp:

The /~ object is optimized to multiply a signal coming into the left inlet by the reciprocal of either the initial argument or an int or float received in the right inlet.

There is no need to recalculate the reciprocal of the initial argument on every signal vector if it hasn't changed. The value just needs to be calculated once (in digits: 1 time) at instantiation time and cached. If a float or int (ie, non-signal) message comes in the right inlet, calculate the reciprocal once (1 time) then and there and update the cache.

Roman Thilenius's icon

but the /~ object could have received a new number during the last vector isnt it ... that
is what i meant, a _maximum of once every vector.

sorry about ignoring the docs. :/ but i have no time to do such regular amazing
things, i have to finish all the extraordinary amazing things first.

maybe i should also look into the gainslider~ source code now, to get the whole picture
about how object do deal with the possibilty of incoming data, and to confirm it totally
that these cycling objects are really to much optimized.

not that i wouldnt trust you but sicher ist sicher.

-110

Peter Castine's icon

A common design pattern in MSP objects that need to calculate auxiliary values dependent upon Max messages is to precalculate those values when the Max message arrives. There's no advantage to waiting for the next signal vector (if anything, there would be disadvantages). So the calculations are completely independent of signal processing and signal vectors and everything else MSP-ish. That is why I took exception to the previous comment.

In short: Max messages are normally processed in the Max event handling thread, not in the DSP processing thread.

At DSP signal vector processing time, at the start of an object's perform routine (ie, the actual signal vector processing), the precalculated values are copied into registers for fast access. So, if the Max message thread interrupts the DSP thread (which can happen), any value changes are written to the object's memory area, but the values in the CPU registers are unaffected during the processing of the current signal vector.

I cannot swear that /~ is doing it like that, although I recall that this approach is discussed in the MSP SDK, either with /~ or one of the other simple MSP objects as an example and source of sample code.

--

Vertrauen ist gut, Kontrolle besser.
Kontrollier' mich ruhig, ich bin nicht unfehlbar. Es ist nur üblicherweise, dass ich Recht habe;-)