Avoiding floating point arithmetic

I wrote a small software synthesizer for the iPhone.
To further tune performance, I measured my application with Shark and found that I was wasting a lot of time in float / SInt16 conversions.
So I rewrote some parts to get around conversions, precomputing lookup tables that return “ready-to-use” SInt16 patterns. So far this is wonderful. I'm currently trying to rewrite some filters and my implementation of the ADSR envelope to use only integer arithmetic, but I could use some tips on how to perform multiplication / division without floats.
I am aiming for the canonical iPhone format :

  • LPCM
  • 16-bit integer samples

What are good approaches to applying amplitude to my final pattern without using float?

Edit:
The only thing I have guessed so far is that I can split by powers of 2 by moving my current sample.

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

But I cannot come up with any elegant way to create a smooth ADSR envelope with this.

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

  • enter all values ​​of the ADSR envelope in the positive SInt16 range
  • multiply by the current value from wavetable (store intermediate products as SInt32)
  • move the result 16 to the right

it works:)

+3
source share
4 answers

, 16 . - 10 . 32- int , . 16- int, , .

: , . (32 64 , ). ,

, . (a * n) * (bn), abn ^ 2 abn. (an)/(bn), (a/b) ((an)/b). 10, , .

, , 16- int. , .

, . http://www.digitalsignallabs.com/fp.pdf

+4

SO . , :

, , [-1.0,1.0]. [-2 ^ 15, (2 ^ 15) -1]. ,

Half = round(0.5*32768); //16384
Third = round((1.0/3.0)*32768); //10923

,

Temp = Half*Third; //178962432
Result = Temp/32768; //5461 = round(1.0/6.0)*32768

32768 - Patros, , . , 2 ^ N:

x1 = x1Float*(2^15);
x2 = x2Float*(2^15);
Temp = x1Float*x2Float*(2^15)*(2^15);
Result = Temp/(2^15); //get back to 2^N scaling

, . , 16- 32- , Temp 32-. , 32768 16- , , 32- . , , / 2,

N = 15;
SInt16 x1 = round(x1Float * (1 << N));
SInt16 x2 = round(x2Float * (1 << N));
SInt32 Temp = x1*x2;
Result = (SInt16)(Temp >> N);
FloatResult = ((double)Result)/(1 << N);

, [-1,1] ? , , [-4.0.4.0), N = 13. 1 , 13 . 1.15 3.13 . .

, . , , . ,

Quotient = (x1/x2) << N;

,

Quotient = (SInt16)(((SInt32)x1 << N)/x2); //x1 << N needs wide storage

. , 6,

Quotient = x1/6; //equivalent to x1Float*(2^15)/6, stays scaled

2,

Quotient = x1 >> 3; //divides by 8, can't do x1 << -3 as Patros pointed out

, , . , x.y, .

, , .

+3

, , 16.16 . 32- 16- 16- . , iPhone (Objective-C ?), C:

#include <stdint.h>

typedef fixed16q16_t int32_t ;
#define FIXED16Q16_SCALE 1 << 16 ;

fixed16q16_t mult16q16( fixed16q16_t a, fixed16q16_t b )
{
    return (a * b) / FIXED16Q16_SCALE ;
}

fixed16q16_t div16q16( fixed16q16_t a, fixed16q16_t b )
{
    return (a * FIXED16Q16_SCALE) / b ;
}

, . , div16q16() , . 64- , . , . , . , .

, - . C , . YMV , .

OO fixed16q16_t, , , .

:

double fixed16q16_to_double( fixed16q16_t fix )
{
    return (double)fix / FIXED16Q16_SCALE ;
}

int fixed16q16_to_int( fixed16q16_t fix )
{
    // Note this rounds to nearest rather than truncates
    return ((fix + FIXED16Q16_SCALE/2)) / FIXED16Q16_SCALE ;
}

fixed16q16_t int_to_fixed16q16( int i )
{
    return i * FIXED16Q16_SCALE ;
}

fixed16q16_t double_to_fixed16q16( double d )
{
    return (int)(d * FIXED16Q16_SCALE) ;
}

, .

+ - .

+1

Source: https://habr.com/ru/post/1718045/


All Articles