Complex numbers instructions


#1

Hi everyone, it's been a while :slight_smile:

I'm writing some code to work with complex numbers in fixed point (you can see the unfinished work in this patch: complex numbers.axp (14.1 KB)

For now there's just a limited set of instructions: (cartesian assign, sum, subtract, multiply, divide, integer power, scale, interpolation, rotation and polar and cartesian output)

I plan to add some other stuff, at least to cover the basics (so a conversion from cartesian to polar and vice versa is the bare minimum).

Is there some other stuff that might be useful?

Update: i've made a list of the functions


#2

That's impressive, especially with fixed point !

There is a lot of funny and experimental stuff to do with complex numbers such as non linear (and yet stable) filters.


#3

I have a problem that may be related as I cannot find math to achieve the result I need.
I have built a footswitch controller with 8 momentary switches, all are interconnected to one Axo analogue input with resistor divider so I get 8 values approx 8, 16, 24, 32, 40, 48, 56, 64. But as it is coming into the Analogue pin there is ripple so the value is always changing and fractioned. And here in lies the problem, to connect these 8 values to a Mux, I divide it by 8 as per bitshift>>3 object, and get values 1 to 8, and to get a cleanest value possible, I add it into the "round" object, but this is still not clean enough. As when it passes through the Mux, it bounces infrequently and randomly. So my only thought is the values are somewhat dirty.
My solution to the problem was to start back with the 0 to 64, and use a list of if statements to achieve a clean value if<4=0, if>4&<12=1, if>12&<20=2 and so on till I get to 8. And this fixes the problem perfectly and efficiently.
However based on this workaround I am now led to believe the math is very different to what I thought it was, and am possibly dealing with very complex values hence my desire to add this here.
Do you have any insight into more efficient math for this ?:smiley:


#4

@gavin I think you're a bit (a lot) off track. Complex numbers are combinations of real and imaginary numbers (and imaginary numbers are multiples of sqrt(-1) ).

@SmashedTransistors Yep, i hoped so! I used complex algebra just for chaotic oscillators, but i feel i'm missing a whole world of awesome functionalities..
I should ask you a question, however: do you have any idea on how to implement an arctan function that's fairly accurate and does not slow things down too much?


#5

get it now, "Complex numbers" and numbers that I find complex.... :flushed:


#6

You can have a look at:
http://dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization
and
http://www.ntu.edu.sg/home/aukil/papers/conf/2011_IEEE-ISIE11_Fast-arctan.pdf

In my jsfx plugin i used a non normalized "diamond angle". It was good enough for my purpose ( self non linear modulation of the rotation speed/frequency of the complex filter ).
The jsfx code can be easily translated to C.

//diamond angle 0..4
function CXF_diamAngle(x y) (
  y >= 0 ? (
    x >= 0 ?     y / (x + y) : 1 + x / (x - y);
  ) : (
    x < 0  ? 2 + y / (x + y) : 3 + x / (x - y); 
  );
);

#7

Thank you a lot! I've updated the patch with the diamond angle function. Just for curiosity, how did it behave in your plugin?


#8

Instruction set:

Data structure: complex_number
It's composed of two variables: int32_t real and int32_t imaginary
Example of variable declaration:

complex_number z1;

cplx_assign(int32_t real , int32_t imaginary , complex_number *z )
Stores inside the variable z (pointer), the complex number formed by the real part "real" and the imaginary part "imaginary".
Example:

cplx_assign(param_R1,param_I1,&z1);

real(complex_number *z )
Returns the real part of the complex number z.
Example:

disp_sumR = real(&z3);

imaginary(complex_number *z )
Returns the imaginary part of the complex number z.
Example:

disp_sumI = imaginary(&z3);

cplx_sum(complex_number *z1 , complex_number *z2 ** , complex_number *z3)**
Performs the sum z1 + z2, stores the result in the variable z3. (Pointers!)
Example:

cplx_sum(&z1,&z2,&z3);

cplx_subtract(complex_number *z1 , complex_number *z2 ** , complex_number *z3)**
Performs the sum z1 - z2, stores the result in the variable z3. (Pointers!)
Example:

cplx_subtract(&z1,&z2,&z3);

cplx_mul_Q27(complex_number *z1 , complex_number *z2 , complex_number *out )
Performs z1 * z2 in fixed point (using Q27 format) and stores the result into the variable "out"

cplx_mul_Q27(&z1,&z2,&z4);

cplx_div_Q27(complex_number *z1 , complex_number *z2 , complex_number *out )
Performs z1 / z2 in fixed point (using Q27 format) and stores the result into the variable "out"

cplx_div_Q27(&z1,&z2,&z5);

cplx_pow_Q27(complex_number *z , int32_t exp , complex_number *out )
Performs z^exp in fixed point (using Q27 format) and stores the result into the variable "out".

cplx_pow_Q27(&z1,param_exp,&z6);


cplx_scale_Q27(complex_number *z , int32_t scale , complex_number *out )
Performs a scalar multiplication between the complex number z and the integer scale (in fixed point, Q27 format) and stores the result into the complex number "out".

cplx_scale_Q27(&z1,param_rate,&z7);

cplx_interp_Q27(complex_number *z1 , complex_number *z2 , int32_t rate , complex_number *out )
Interpolates linearly between two complex numbers z1 and z2 and stores the result into the variable "out". The amount of interpolation is set by the integer "rate".
Rate = 0 outputs z1
Rate = (1<<27) outputs z2

cplx_interp_Q27(&z1,&z2,param_rate,&z8);

cplx_rotate(complex_number *z , uint32_t phase , complex_number *out )
Rotates the complex number z by an amount "phase" and stores the result in the variable "out".
The phase is expressed in unsigned int.
phase = 0 corresponds to 0
phase = (1<<29) corresponds to pi/4
phase = (1<<30) corresponds to pi/2
phase = (1<<31) corresponds to pi
phase = (1<<32) corresponds to 2pi

cplx_rotate(&z1,param_rate<<5,&z10);

cplx_rotate_cheap(complex_number *z , uint32_t phase , complex_number *out )
Rotates the complex number z by an amount "phase" and stores the result in the variable "out".
The phase is expressed in unsigned int.
This one is slightly cheaper than the previous, it just reads the sine2t table, without interpolation.

cplx_rotate_cheap(&z1,param_rate<<5,&z10);

cplx_polar_Q27(complex_number *z , int32_t *modulo , uint32_t *phase )
Converts the complex number z in polar form, and stores the components in the integer variables "modulo" and "phase".
Phase follows the format i described above for the rotate function.

cplx_polar(&z10,&disp_mod,&phase);

cplx_cartesian(complex_number *z , int32_t *real_out , int32_t *imaginary_out )
Outputs the complex number z in cartesian form in the variables real_out and imaginary_out.
It's really just a condensation of the real() and imaginary() functions..

cplx_cartesian(&z4,&disp_mulR,&disp_mulI);

#9

A complex resonator allows high Q and is very stable :
https://ccrma.stanford.edu/~jos/smac03maxjos/smac03maxjos.pdf

I used the diamond Angle to perform a non linear self modulation of this kind of resonator.
The main advantage of the complex resonator is its stability, even when it is modulated at audio rate.

Here is an example:

The plugin synth is based on sine oscillators processed by such "self modulated resonators".

Even if the diamond angle is not a numerically perfect atan, i think it is very useful for audio rate processing.

I believe that complex numbers can be very useful for non linear feedback systems because they can help with the normalisation issues as we have access to argument and modulus (i.e. angle and amplitude).


#10

would you share your patch please?


#11

Hi @philoop,

This is not an patch (yet) it is a Reaper JSFX plugin.