login

<     >

2021-01-16 20:38:06 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

add support for pitch wheel

diff --git a/qms.c b/qms.c
index 007ef61..8d6fb77 100644
--- a/qms.c
+++ b/qms.c
@@ -9,7 +9,9 @@ typedef struct TrackState {
 typedef struct VoiceState {
     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 velocity;    /* 0x00-0x7F */
+    int pitch;       /* 0x00-0x7F */
+    int wheel;       /* (-0x2000)-(+0x1FFF)  default (zero) means center */
     /* int note_age; */
 } VoiceState;
 
@@ -83,15 +85,17 @@ qms_setpan(int track, int midipan)
 }
 
 static unsigned int
-midipitch2step(int m)
+midipitch2step(int m, int w)
 {
     unsigned int o, n; /* m = o * 12 + n */
+    uint32_t nwc;
     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;
+    nwc = ((n<<8)+((w>>4)-0x200))*(c>>8);
+    return ((fxp_expm1(nwc)>>5) * 22 / (2205<<6) + (22<<21) / 2205) << o;
 }
 
 void
@@ -103,7 +107,17 @@ qms_setvelocity(int track, int voice, int velocity)
 void
 qms_setnote(int track, int voice, int midipitch)
 {
-    voices[track][voice].phase_step = midipitch2step(midipitch);
+    int midiwheel = voices[track][voice].wheel + 0x2000;
+    voices[track][voice].phase_step = midipitch2step(midipitch, midiwheel);
+    voices[track][voice].pitch = midipitch;
+}
+
+void
+qms_setwheel(int track, int voice, int midiwheel)
+{
+    int midipitch = voices[track][voice].pitch;
+    voices[track][voice].phase_step = midipitch2step(midipitch, midiwheel);
+    voices[track][voice].wheel = midiwheel - 0x2000;
 }
 
 void
@@ -170,6 +184,9 @@ qms_runevents(Event *evs, unsigned int nevs)
             break;
         case PITCH:
             qms_setnote(track, voice, arg);
+            break;
+        case WHEEL:
+            qms_setwheel(track, voice, arg);
         }
     }
 }

diff --git a/qms.h b/qms.h
index e2583e8..940e5b7 100644
--- a/qms.h
+++ b/qms.h
@@ -15,7 +15,7 @@
 /* sample rate in samples per second*/
 #define R       44100
 
-typedef enum EvType {END, PAC, VOL, PAN, VEL, PITCH} EvType;
+typedef enum EvType {END, PAC, VOL, PAN, VEL, PITCH, WHEEL} EvType;
 
 typedef struct Event {
     uint32_t offset;
@@ -27,6 +27,7 @@ typedef struct Event {
 #define qms_ev_pan(t, pan)      (((t) << 28) | (PAN << 16) | (pan))
 #define qms_ev_vel(t, v, vel)   (((t) << 28) | ((v) << 24) | (VEL << 16) | (vel))
 #define qms_ev_pitch(t, v, pit) (((t) << 28) | ((v) << 24) | (PITCH << 16) | (pit))
+#define qms_ev_wheel(t, v, whl) (((t) << 28) | ((v) << 24) | (WHEEL << 16) | (whl))
 
 void qms_init();
 void qms_setpac(int track, int pac);
@@ -34,6 +35,7 @@ void qms_setvol(int track, int midivol);
 void qms_setpan(int track, int midipan);
 void qms_setvelocity(int track, int voice, int velocity);
 void qms_setnote(int track, int voice, int midipitch);
+void qms_setwheel(int track, int voice, int midiwheel);
 void qms_advance(unsigned int nsamples);
 void qms_runevents(Event *evs, unsigned int nevs);
 void qms_putsample(int16_t left, int16_t right);

diff --git a/smf.c b/smf.c
index 2b73a24..6dd2f7b 100644
--- a/smf.c
+++ b/smf.c
@@ -89,6 +89,8 @@ qms_smf2evs(const char *fname, Event *evs, int maxnevs, int *pnevs)
     uint32_t usecs_per_quarter;
     uint32_t offset;
     uint8_t byte, status, chan, arg, vel;
+    uint16_t param, wheel;
+    uint8_t semirange, centrange;
     int smf_track;
     int ntcs, nevs;
     ntcs = nevs = 0;
@@ -103,6 +105,9 @@ qms_smf2evs(const char *fname, Event *evs, int maxnevs, int *pnevs)
     if (division & 0x8000) return SMF_BADDIV;
     ticks_per_quarter = division;
     status = 0; /* not meaningful, just to initialize variable */
+    param = 0;
+    semirange = 2;
+    centrange = 0;
     for (smf_track = 0; smf_track < ntracks; smf_track++) {
         if (read_beu32(fd) != 0x4D54726B) return SMF_BADSIG;
         data_length = read_beu32(fd);
@@ -155,12 +160,28 @@ qms_smf2evs(const char *fname, Event *evs, int maxnevs, int *pnevs)
                     break;
                 case 0xB:   /* control change */
                     switch (arg) {
+                    case 0x06:  /* data entry MSB */
+                        if (param == 0x0000)    /* pitch bend range */
+                            semirange = read_u8(fd);
+                        else (void) read_u8(fd);
+                        break;
                     case 0x07:  /* channel volume */
                         add_ev(qms_ev_vol(chan, read_u8(fd)));
                         break;
                     case 0x0A:  /* channel pan */
                         add_ev(qms_ev_pan(chan, read_u8(fd)));
                         break;
+                    case 0x26:  /* data entry LSB */
+                        if (param == 0x0000)    /* pitch bend range */
+                            centrange = read_u8(fd);
+                        else (void) read_u8(fd);
+                        break;
+                    case 0x64:  /* RPN LSB */
+                        param = (param & 0xFF00) | read_u8(fd);
+                        break;
+                    case 0x65:  /* RPN MSB */
+                        param = (param & 0x00FF) | (read_u8(fd) << 7);
+                        break;
                     default:    /* other control change (ignore) */
                         (void) read_u8(fd);
                     }
@@ -168,8 +189,10 @@ qms_smf2evs(const char *fname, Event *evs, int maxnevs, int *pnevs)
                 case 0xC:   /* program change */
                     add_ev(qms_ev_pac(chan, arg % NPACS));
                     break;
-                case 0xE:   /* pitch wheel change (ignore extra arg) */
-                    (void) read_u8(fd);
+                case 0xE:   /* pitch wheel change */
+                    wheel = arg | (read_u8(fd) << 7);
+                    wheel = ((((int16_t) wheel >> 1) - 0x1000) * semirange) + 0x2000;
+                    add_ev(qms_ev_wheel(chan, 0, wheel));
                 }
             }
             if (nevs == maxnevs - 1) return SMF_TOOBIG;