Q-formats used in axoloti_filters


#1

Hi

I got my Axoloti this week and I am really impressed, especially by the audio quality, the live mode and how fast a patch is compiled and downloaded.

Today I wrote my first custom object. I used bp.axo and the function biquad_bp_coefs from axoloti_filters.h as starting point and was able to make a notch-filter object :smile:
... but without understanding al lot of the code, especially the fractional numbers.

Near the surface, the Q5.27-format seems to be used. I came to this conclusion while playing with controlls and hex-displays, where:
0x07FFFFFF * 0x07FFFFFF == 0x07FFFFFE
... so 0x07FFFFFF must be interpreted as one.

I come to the same conclusion while looking at the s-rate code in STAR.axo:
outlet_result= ___SMMUL(inlet_a<<3,inlet_b<<2);
... so during multiplication a left-shift of 5 positions is needed.

But when I look at method biquad_bp_coefs in axoloti_filters.h, then the term
(1.0 + alpha) is implemented as: (HALFQ31 + alpha),
where the constant HALFQ31 has the value 0x40000000,
so this calculation seems to use the Q2.30-format.

And finally in method biquad_dsp in axoloti_filters.h, the Q4.28-format seems to be used, because after the SMMLA-sequence, the result is shifted to the left about 4 positions:

  for (i = 0; i < BUFSIZE; i++) {
    int32_t filterinput = inbuffer[i];
    int32_t accu = ___SMMUL(coefs->cxn_0, filterinput);
    accu = ___SMMLA(coefs->cxn_1, filter_x_n1, accu);
    accu = ___SMMLA(coefs->cxn_2, filter_x_n2, accu);
    accu = ___SMMLS(coefs->cyn_1, filter_y_n1, accu);
    accu = ___SMMLS(coefs->cyn_2, filter_y_n2, accu);
    int32_t filteroutput;
    filteroutput = accu << 4;
    filter_x_n2 = filter_x_n1;
    filter_x_n1 = filterinput;
    filter_y_n2 = filter_y_n1;
    filter_y_n1 = filteroutput;
    outbuffer[i] = __SSAT(filteroutput, 28);
  }

On the other hand, the filter-output is saturated on position 28, as in the saturation object sat.axo (which would indicate Q5.27).

I need some help to see the concept, e.g. where the format has to be converted. Thanx very much.

Jan

P.S. My next goal is to implement a parametric equalizer object


#2

@johannes do you have any thoughts to share on this?


#3

I figured out a lot by myself. At the beginning I was puzzled why the filter coefficient weren't stored in Q1.31 to get the highest possible quality. But the filter-coefficients can be larger than one, so you need some headroom.


#4

ah, so just a matter of knowing the convention for different data I suppose...
e.g. 5.27 for b/p control rate floats, 1.31 for audio , and 4.28 for filter co-eff ?

I guess, it just needs to use whichever representation is the best fit for the data, and then convert to the relevant convention for the output to outlets.

Ive also been 'struggling' with some of this stuff, so its good to see some posts about it, its helping me to clarify some of it in my head :smile:


#5

I'm going to try to make some helpful comments in my code. It's especially confusing if a bitshift operation has more than one intention, e.g. if
x <<= 2;
stands for: switch from q5.27 to q4.28 AND multiply by 2. :no_mouth:


#6

Q5.27 is indeed used for control rate and audio rate in- and outlets.
Parameters before transform (pfunction) are also Q5.27
Objects can use whatever representation they want internally.

If you add comments, I'd be happy to take a git pull request!


#7

As a start I'm trying to comment my own contributions to the community library :smile: