Search code examples
iphonecore-audiosignal-processingfixed-pointinteger-division

Avoiding floating point arithmetic


I wrote a small software synth for the iPhone. To further tune performance I measured my application with Shark and found that I am losing a lot of time in float/SInt16 conversions.

So I rewrote some parts to get around the conversions by pre-calculating lookup tables that return "ready-to-use" SInt16 samples. This works fine so far.

Currently I am trying to rewrite some filters and my ADSR envelope implementation to use only integer arithmetic but I could use some tips on how to perform multiplications/divisions without floats.
I am targetting the iPhone canonical format:

  • LPCM
  • 16-bit integer samples

What are good approaches to apply an amplitude to my final sample without using a float?

Edit:
The only thing I figured out so far is, that I can divide by powers of 2 by right-shifting my current sample.

inBuffer[frame] = wavetable[i % cycleLengthInSamples] >> 4;

But I can't think of any elegant way to create a smooth ADSR envelope with that.

Edit2: Thanks for all your great answers!
My current approach:

  • bring all my ADSR envelope values into the positive SInt16 range
  • multiply with the current value from the wavetable (store intermediates as SInt32)
  • shift the result by 16 to the right

this seems to work :)


Solution

  • Fixed point is good, since in this case you're using 16 bits. The simplest way is to multiply by a power of 10 depending on the precision you need. If you can use 32 bit ints as an intermediate, you should be able to get decent precision. At the end you can convert back to a 16 bit int, rounding or truncating as you prefer.

    Edit: You want shift to the left, to make the values bigger. Store the result of the shift in a type with more precision (32 or 64 bit depending on what you need). simple shifting won't work if you are using signed types

    Watch out if you're multiplying or dividing two fixed point numbers. Multiplying winds up being (a*n) * (bn) and you'll wind up with abn^2 instead of abn. Division is (an) / (bn) which is (a/b) instead of ((an)/b). That's why I suggested using powers of 10, makes it easy to find your mistakes if you're not familiar with fixed point.

    When you're done your calculations, you shift back to the right to get back to a 16 bit int. If you want to get fancy, you can also do rounding before you shift.

    I suggest you do some reading if you're really interested in implementing efficient fixed point. http://www.digitalsignallabs.com/fp.pdf