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;