nigel WaveTable - another question

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

Post

Trying to figure out how to process custom waveform starting with frequency domain.

Taking nigel nice tutorial: https://www.earlevel.com/main/2013/03/0 ... avetables/

How would you change the second harmonic (for example) by 90°?

Tried this:

Code: Select all

int secHarm = 2
freqWaveIm[secHarm] = M_PI;
freqWaveIm[tableLen - secHarm] = -freqWaveIm[secHarm];
But it output different signal than expected.

Where I'm wrong? Isn't pi in second and mirror index in the im array?

Cheers

Post

Hi markzzz,

I assume you know how complex numbers work, and you've just mixed up a couple of things (otherwise it'd probably help to run through some more maths reading before playing with frequency domain and phase).

You're creating a complex number from real and imaginary parts, so you want something like:

Code: Select all

double amp = 1;
double phase = M_PI/2;
freqWaveRe[secHarm] = amp*cos(phase); // will be 0 in this case
freqWaveIn[secHarm] = amp*sin(phase); // will be `amp` in this case
// and then the complex conjugate for the negative frequencies
freqWaveRe[tableLen - secHarm] = amp*cos(phase);
freqWaveIn[tableLen - secHarm] = -amp*sin(phase); // -amp
(BTW, 90° in radians is is π/2, not π, hence the value for "phase".)

Because we know the complex phase is π/2, we could have skipped the sin()/cos() and just stuck in 0/amp/-amp directly.

Geraint

Post

I see, thanks!
Later I'll try.

A thing: I suppose the "general" use is:
freqWaveRe[tableLen - secHarm] = -amp*cos(phase);

Right? I don't see the minus in your code for the mirror RE... maybe a typo?

Post

Not a typo - complex conjugate (for the negative-frequency components) mean the imaginary part is flipped, but the real part is not. If you do anything else, you'd end up with an imaginary or complex output result after the IFFT.

Post

It might be easier to work with real (to half-complex) FFTs here so you don't need to worry about the conjugates.

Post

signalsmith wrote: Sun Mar 21, 2021 8:00 pm Not a typo - complex conjugate (for the negative-frequency components) mean the imaginary part is flipped, but the real part is not. If you do anything else, you'd end up with an imaginary or complex output result after the IFFT.
It seems Nigel example (i.e. saw) flip the real part, while keeping each im at zero...

Post

That should generate an entirely imaginary result - but I can't find Nigel's makeWaveTable(), so perhaps he's swapped something else around in there which I wouldn't expect.

But yeah, as mystran said, real FFTs are probably your friend here.

Post

markzzz wrote: Sun Mar 21, 2021 8:28 pmIt seems Nigel example (i.e. saw) flip the real part, while keeping each im at zero...
Gee, it's been a long time...I'm wondering if the FFT routine I used in the example had the real/im swapped. I just grabbed it from somewhere to have a simple FFT in the code. Offhand it seems it should be in the im part, as signalsmith said, since that's the sine part. So it does kind of sound like they are swapped. (FWIW, in this particular case, you could actually leave the mirrored part zeroed out in both real and im parts, since it would result in no negative frequencies hence half amplitude signal—the code autoscales anyway so you end up with the same thing.)
My audio DSP blog: earlevel.com

Post

Tried this:

Code: Select all

double amp = 1;
double phase = M_PI;

freqWaveRe[idx] = amp * cos(phase);
freqWaveRe[tableLen - idx] = -freqWaveRe[idx];
freqWaveIm[idx] = amp * sin(phase);
and it works :wink:
If I add also this "freqWaveIm[tableLen - idx] = -freqWaveIm[idx];", it seems nothing change.

mystran wrote: Sun Mar 21, 2021 8:17 pm It might be easier to work with real (to half-complex) FFTs here so you don't need to worry about the conjugates.
What do you mean with this? Different libraries? Can you show to me an example?

Post

markzzz wrote: Mon Mar 22, 2021 8:58 am
mystran wrote: Sun Mar 21, 2021 8:17 pm It might be easier to work with real (to half-complex) FFTs here so you don't need to worry about the conjugates.
What do you mean with this? Different libraries? Can you show to me an example?
For real-only signals the positive and negative frequencies are always complex conjugate of each other (ie. half the information is redundant), so many (most?) FFT libraries come with special "real FFT" (and it's inverse) that takes a real-valued signal and transforms into a half-sized complex spectrum that only contains the positive frequencies (and the inverse transforms back to real-valued signal as if the negative frequencies were conjugates of the positive ones provided).

As it turns out, such a transform is also only slightly more expensive than a half-sized complex transform (ie. typically almost twice as fast), although performance is probably rather irrelevant when precomputing wavetables.

Post

markzzz wrote: Mon Mar 22, 2021 8:58 am

Code: Select all

double phase = M_PI;
and it works :wink:
If I add also this "freqWaveIm[tableLen - idx] = -freqWaveIm[idx];", it seems nothing change.
That's because (as I said above) you're using M_PI, not M_PI/2. You said you wanted to rotate the phase by 90°, right?

I think you've rotated by 180° instead, which is just flipping the ± sign.

Post

signalsmith wrote: Mon Mar 22, 2021 11:32 am
markzzz wrote: Mon Mar 22, 2021 8:58 am

Code: Select all

double phase = M_PI;
and it works :wink:
If I add also this "freqWaveIm[tableLen - idx] = -freqWaveIm[idx];", it seems nothing change.
That's because (as I said above) you're using M_PI, not M_PI/2. You said you wanted to rotate the phase by 90°, right?

I think you've rotated by 180° instead, which is just flipping the ± sign.
You were right, true!
This seems working fine:

Code: Select all

freqWaveRe[idx] = amp * std::cos(phase);
freqWaveRe[tableLen - idx] = -freqWaveRe[idx];
freqWaveIm[idx] = amp * std::sin(phase);
freqWaveIm[tableLen - idx] = freqWaveIm[idx];
Thanks

Post Reply

Return to “DSP and Plugin Development”