Why won't the for loop do what I want it to?
I'm rather new to programming, and I've been testing out making an additive synth with codebox in gen~. To generate all of the partials, I figured it would be best to use a for loop to call a function for each partial at their respective frequencies and amplitudes. It sounds really crazy when I do that, it seems like it gives me one sine wave at a really high frequency.
When I call a function for each partial individually and add and assign at the output (like out += sine1, 2, 3...) it works fine.
What am I doing wrong?
the codebox in the parent patch is just a reference of a patch that uses a for loop to generate grains in a granular synth
bump for anyone else that has more time/know to help here. i took a look and found there's a divide-by-zero('1/i'), so...
...this would work the same as the code you have, but it still doesn't fix your problem:
sine(f, m, a, r){
fm = f * m;
phase = phasor(fm, r, phase = 0.75);
return cycle(phase, index="phase") * clip(a, 0, 1);
}
Param freq(100), mult(1), amp(0.5);
trig = in1; out1 = 0; partials = 16; x = 0;
for(i = 0; i < partials; i += 1){
out1 += sine(freq, i+1, 1/i+1, trig);
}
(i wonder if it doesn't work because the for-loop causes the 'sine' function to restart its phasor every sample.... as opposed to letting them run free the other way? perhaps you can remove the phasor from the function and control all from external phasor(you can add a wrapped modulo op within to have different rates spawn from same phasor, etc.), then you can still shorten code with a bit of for-loop?)
I didn't think about the /0 thing, thanks for pointing that out.
I'll try the phasor thing, I'd rather not use a phasor for each partial anyway. But the thing is I want to be able to control the frequencies of each partial by bunching them up close together and spreading them apart, and then being able to reset the phase of each of them. Do you happen to know if there's a way to do that if I use only a master phasor?
basically I'm trying to remake the following patch into something more efficient lol
I think you were right about the for loop resetting the phasor on each iteration. I got it working with the wrapped phasor, the partial phases aren't right so it's not ideal but I'll do some experimenting.
Do you happen to know if there's a way to do that if I use only a master phasor?
possibly by adding an offset at the right moment(a history can keep track of the difference between the master phasor and it's wrapped version, and then add a compensating offset at the moment you want the reset)... hard to describe in words, but my best guess would be something like that.
that's a great patch! (it doesn't use any more than 2% CPU on my laptop, but if you need many voices along with other stuff, it can make sense, reducing all the phasors to one will definitely help)
Awesome, thanks for your advice!
It may help to think of it this way: when you put cycle()
into a for loop over N iterations, it is like you are running the same oscillator N times, not that you are creating N oscillators. It is more like upsampling the oscillator.
Some previous posts on a similar topic, with explanations as to what is going on and why it works this way:
By the way, if you are only working with integer harmonics, I highly recommend looking into the CORDIC algorithm -- it is a lot cheaper -- and you can use the looping behaviour to your advantage. Here's a very basic version, but I recommend looking into the complex form of this (gives you quadrature output, and is actually probably cheaper by avoiding sin() calls):
// fundamental phasor:
hz = 110;
base = phasor(hz);
// the highest harmonic:
N = 20;
out1 = 0;
phase = 0;
for (i = 1; i <= N; i += 1) {
// phase for this harmonic (no need to wrap, because sin() does it for you)
phase += base;
// signal value for this harmonic:
s = sin(twopi * phase);
// harmonic level to create a sawtooth:
s = s / (i * pi);
out1 += s;
}
Here's a complex form of CORDIC, along with a little fade out to prevent harmonics above the Nyquist limit. 256 harmonics is using about 3% cpu on my laptop. It also takes some pretty deep FM :-)
Hey Graham! Yeah, the main thing I like about additive synthesis is being able to use non-integer harmonics. But I’ll check these out, it looks interesting. Thanks for the resources.
Just got around to looking into these, some really good information in the other posts.
For anyone finding this who's interested in using a for loop to mix sines to an output, I'll repost some code Graham shared from this post that uses Data to store the phases, I modified it a bit and added 16 partials:
// fundamental frequency
Param freq(100, min = 0, max = samplerate);
// number of voices
// any integer up to the length of the data
Param partials(16, min = 1, max = 256);
// stores phase, freq in each data frame
Data phases(16);
sum = 0;
for (i = 0; i < partials; i += 1) {
// get frequency for this voice:
f = freq * (i + 1);
// get state for this voice:
phase = peek(phases, i);
// equivalent to phasor(freq):
phase = wrap(phase + (f / samplerate), 0, 1);
// write phase back into [data]:
poke(phases, phase, i);
// do the additive voice:
sum += (sin(phase * twopi)) / (i + 1);
}
out = dcblock(sum / 2);