login

<     >

2021-01-09 21:32:53 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

replace look-up table by power series approx for freq

diff --git a/qms.c b/qms.c
index b85caae..97e033a 100644
--- a/qms.c
+++ b/qms.c
@@ -7,8 +7,8 @@ typedef struct TrackState {
 } TrackState;
 
 typedef struct VoiceState {
-    unsigned int phase_acc;   /* fixed point with NEXP.NEXP precision */
-    unsigned int phase_step;  /* fixed point with NEXP.NEXP precision */
+    unsigned int phase_acc;   /* fixed point with NEXP.16 precision */
+    unsigned int phase_step;  /* fixed point with NEXP.16 precision */
     int velocity;    /* 0-127 */
     /* int note_age; */
 } VoiceState;
@@ -17,13 +17,6 @@ static int16_t wavetables[NPACS][N];
 static TrackState tracks[NTRACKS];
 static VoiceState voices[NTRACKS][NVOICES];
 
-/* integer frequencies of MIDI notes 0-11 multiplied by N
- * to be used as fixed point with NEXP.NEXP precision
- * python: [int(440 * 2**((m-69)/12) * N + 0.5) for m in range(12)] */
-static unsigned int freq[12] =
-  {16744, 17740, 18795, 19912, 21096, 22351,
-   23680, 25088, 26580, 28160, 29834, 31609};
-
 /* sine approximation using Bhaskara I's formula
  * input is in [0;2048]
  * output is in [-INT16_MAX;INT16_MAX] */
@@ -39,6 +32,22 @@ sinb1(unsigned int i)
     return s * (int16_t) (n/d);
 }
 
+/* approximation of exp(x)-1 using power series
+ * both input and output are U0.32 */
+uint32_t
+fxp_expm1(uint32_t x)
+{
+    uint32_t e, t;
+    int i;
+    e = 0;
+    t = x;
+    for (i = 2; t; i++) {
+        e += t;
+        t = (t>>13) * (x>>19) / i;
+    }
+    return e;
+}
+
 void
 qms_init()
 {
@@ -77,8 +86,12 @@ static unsigned int
 midipitch2step(int m)
 {
     unsigned int o, n; /* m = o * 12 + n */
-    for (n = m, o = 0; n >= 12; n -= 12, o++) ;
-    return (freq[n] << (o+NEXP)) / R;
+    uint32_t c = 0x0EC98200; /* ln(2^(1/12)) in U0.32 */
+    for (n = m+3, o = 0; n >= 12; n -= 12, o++) ;
+    /* the following takes advantage of assumed constants:
+     *   R = 44100 = 440 * 2205 / 22
+     *   N = 2048 = 2^11 */
+    return ((fxp_expm1(n*c)>>5) * 22 / (2205<<6) + (22<<21) / 2205) << o;
 }
 
 void
@@ -112,11 +125,11 @@ qms_advance(unsigned int nsamples)
             pac = wavetables[track->pac];
             for (vi = 0; vi < NVOICES; vi++) {
                 voice = &voices[ti][vi];
-                amp = pac[voice->phase_acc >> NEXP] * voice->velocity >> 7;
+                amp = pac[voice->phase_acc >> 16] * voice->velocity >> 7;
                 left  += amp * lvol >> 14;
                 right += amp * rvol >> 14;
                 voice->phase_acc += voice->phase_step;
-                voice->phase_acc &= (1 << (NEXP << 1)) - 1;
+                voice->phase_acc &= (1 << (NEXP + 16)) - 1;
             }
         }
         /* saturate before casting down to 16-bit to avoid bad behavior on overflow */