nigel WaveTable - another question

DSP, Plug-in and Host development discussion.
KVRer
13 posts since 16 Mar, 2021

Post Fri Mar 19, 2021 11:21 am

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

KVRist
32 posts since 5 Jul, 2018 from Cambridge, UK

Post Sun Mar 21, 2021 2:13 am

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

KVRer

Topic Starter

13 posts since 16 Mar, 2021

Post Sun Mar 21, 2021 2:59 am

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?

KVRist
32 posts since 5 Jul, 2018 from Cambridge, UK

Post Sun Mar 21, 2021 12: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.

KVRAF
6237 posts since 12 Feb, 2006 from Helsinki, Finland

Post Sun Mar 21, 2021 12: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.
Preferred pronouns would be "it/it" because according to this country, I'm a piece of human trash.

KVRer

Topic Starter

13 posts since 16 Mar, 2021

Post Sun Mar 21, 2021 12:28 pm

signalsmith wrote:
Sun Mar 21, 2021 12: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...

KVRist
32 posts since 5 Jul, 2018 from Cambridge, UK

Post Sun Mar 21, 2021 1:03 pm

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.

KVRian
584 posts since 4 Apr, 2010

Post Sun Mar 21, 2021 8:32 pm

markzzz wrote:
Sun Mar 21, 2021 12:28 pm
It 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

KVRer

Topic Starter

13 posts since 16 Mar, 2021

Post Mon Mar 22, 2021 12:58 am

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 12: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?

KVRAF
6237 posts since 12 Feb, 2006 from Helsinki, Finland

Post Mon Mar 22, 2021 1:36 am

markzzz wrote:
Mon Mar 22, 2021 12:58 am
mystran wrote:
Sun Mar 21, 2021 12: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.
Preferred pronouns would be "it/it" because according to this country, I'm a piece of human trash.

KVRist
32 posts since 5 Jul, 2018 from Cambridge, UK

Post Mon Mar 22, 2021 3:32 am

markzzz wrote:
Mon Mar 22, 2021 12: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.

KVRer

Topic Starter

13 posts since 16 Mar, 2021

Post Fri Mar 26, 2021 7:32 am

signalsmith wrote:
Mon Mar 22, 2021 3:32 am
markzzz wrote:
Mon Mar 22, 2021 12: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

Return to “DSP and Plug-in Development”