Additive synthesis help

Francesco Cameli's icon

Hello everyone,
I am trying to build a simple additive synthesis oscillator inside gen. My code relies around the presence of a for loop that loops around for the number of the harmonics of the oscillator to output the final result. I am trying to control the amplitude of each partial from outside gen by using a buffer whose length in sample is equal to the number of harmonics . The amplitude values are recorded with a multislider + poke combo. This way I can just access the amplitude of each partial by peeking it inside the for loop. The only problem that I am having right now is that when I move the multislider, and therefore updating the amplitude values, I hear clicks. This happens because I would need some kind of interpolation between successive values of the same amplitude, but I can't seem to solve this problem inside the for loop. Does anyone know how could I solve this? I attach two patches: the first one is the one with the problem, while the second one is a not so efficient solution using the data object and storing the values of all the partials at any time, regardless the number of harmonics of the oscillator.

FIRST PATCH:

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

SECOND PATCH:

mzed's icon

It isn't clicking for me. Something about your audio settings? ...?

Francesco Cameli's icon

Is the first patch not clicking when you move the multisliders, especially when jumping between values? The second patch is not supposed to be clicking, it is a solution that I came up with, but it is quite inefficient.

brendan mccloskey's icon

Sorry, no clicking here either (in the first example). Very nice patch btw

Brendan

mzed's icon

Exactly. I like your first patch, and it doesn't click.

Peter McCulloch's icon

Couple of other options:
You can also use ioscbank~, which allows you to do interpolation on the gain.

Also, poly~, where each voice is a cycle~ and the fundamental frequency comes in via in~ can also work well. If phase correlation is important, you can send in an unwrapped phasor~-like signal into the right inlet of cycle~ 0. (Driving it with a continuous phase change rather than a frequency)

You'd build that with something like sig~ 440 into !/~ 1. into +=~ into poly~. It's a more exotic solution, but works well when you need to model something that is synchronous. I use it, for example, to model the driveshaft of a Hammond organ.

Francesco Cameli's icon

Hi Peter,
I tried the poly~ approach by using a combination of phasor~ and rate~ to scale the frequency of each partial, but, even is it was sounding correct, I noticed that phases were not correlated when checking it on a scope~.
I need to try the your last suggestion, though.
The only problem that I would have is that I would need to set the number of partials beforehand as instances of the poly~, while I wanted to have a parameter to dynamically control it, as I tried to achieve in the gen~ example. I could maybe set a high number of poly~ instances and just handle the partials that I need by muting the voices that I don't need inside poly~. I'll investigate further, thanks a lot for the suggestions.

Peter McCulloch's icon

Hi Francesco,

Yeah, rate~ will probably not work for this, but the unwrapped phasor should. Here's a couple of tweaks to the code you posted. I've added interpolation via vectral~. It's not perfect, but it eliminates most zipper noise. I also added stretch tuning as an option. I'm not 100% that this is the best way of implementing it--if anything, you'd be better off doing the math inside of a JS object since it's numbers are 64-bit and this extra precision is helpful when summing up a bunch of numbers. This will make zipper noise when you're adjusting the stretch factor; you could prevent that by giving each overtone its own phasor, but that adds overhead.

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

Francesco Cameli's icon

Thank you a lot for your solution, Peter.
I have never used the vectral~ object before, but it looks like it could be the solution to my problem. If I understood its use correctly, is it basically doing a filtering (in this case a rampsmooth) entry by entry on an array of values? I see on the help file that it is mostly used for FFT envelope following as a kind of spectral rampsmooth~ , is it correct?