/*
  MidiSynth, MIDI synthesiser, a repacement for the Acorn MIDI module

  midi.c - MIDI functions.

  created  16/11/085
  1.0      14/06/14
*/

#include "main.h"
#include "kbd.h"

//#define PROGRAM_UPDATES_BANKS // bank messages take effect on the next program change

// syn.m_in.count data counter values when accumulating 14bit values
#define LSB 2
#define MSB 1


// default midi channel parameters (single channel)
const chan_t def_chan =
{
   {0,0,0},   // patch: bank hi, bank lo, program
   {0,0,0},   // patch (temp): bank hi, bank lo, program
         0,   // channel pressure
    0x2000,   // pitch wheel (mid)
// controllers
         0,   // mod wheel
    0x2000,   // breath
    0x2000,   // foot pedal
         0,   // portamento time (min)
    0x3fff,   // volume (max)
    0x2000,   // balance (mid)
    0x2000,   // pan position (mid)
    0x3fff,   // expression (max)
      0x40,   // vibrato_rate,  (mid no change)
      0x40,   // vibrato_depth, (mid no change)
      0x40,   // vibrato_delay, (mid no change)
         0,   // portamento ctrl (min)
         0,   // switches (all off)
         0,   // switches delta
// parameters
      NULL,   // *param ptr        pointer to current parameter (unassigned)
    0x3fff,   // reg param         parameter number (unassigned)
    0x0100,   // pitch bend range  parameter[0], = 02/00 to give +-2 semitones
    0x0040,   // mod depth range   parameter[5], = 00/40 to give +-50 cents (01/00 = +-1 semitone)
// derived controls
    0x2000,   // bender            scaled pitch wheel (mid)
         0,   // mod               scaled pitch modulation (min)
         0,   // glide             scaled portamento time (min)
    0x3fff,   // volume * expression, square law (max)
    0x2D41,   // left volume  = pan * balance in mid position
    0x2D41    // right volume = pan * balance in mid position
};


const char note[][4] = {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};

// pan/balance law = sin(0..PI/2)*(1<<Q), Q=14, only uses MSB of data
const unsigned short int pantab[MIDI_DATA_RANGE] =
{
      0,  202,  405,  607,  810, 1012, 1214, 1416, 1618, 1820, 2021, 2222, 2422, 2623, 2822, 3022,
   3221, 3419, 3617, 3814, 4011, 4207, 4403, 4598, 4792, 4985, 5178, 5370, 5561, 5751, 5940, 6129,
   6316, 6503, 6688, 6873, 7056, 7238, 7420, 7600, 7779, 7956, 8133, 8308, 8482, 8655, 8826, 8996,
   9165, 9332, 9498, 9663, 9825, 9987,10147,10305,10462,10617,10770,10922,11073,11221,11368,11513,
  11656,11798,11937,12075,12211,12345,12478,12608,12737,12863,12988,13110,13231,13349,13466,13580,
  13692,13802,13911,14017,14120,14222,14322,14419,14514,14607,14698,14786,14872,14956,15038,15117,
  15194,15268,15341,15411,15478,15544,15606,15667,15725,15781,15834,15885,15933,15979,16023,16064,
  16102,16138,16172,16203,16232,16258,16282,16303,16322,16338,16352,16363,16372,16378,16382,16383
};

// portamento rate table in cents/second, only uses MSB of data, from MIDI level 2 spec.
// glide_tc = (1<<Q) - (((1<<Q) * port_rate_table[CC05]) / (scale_factor * 1000)), Q=15
//          = 32768 - ((32768 * port_rate_table[CC05]) / (scale_factor * 1000))
//          = 32768 - ((32 * port_rate_table[CC05]) / scale_factor    (approximation)
static const unsigned int port_rate_table[MIDI_DATA_RANGE] =
{
  552942,267335,157703,104660, 76089, 58063, 46065, 37817,
   31664, 27518, 23892, 21167, 19117, 17182, 15929, 14791,
   13878, 13222, 12600, 12045, 11599, 11152, 10547, 10192,
    9665,  9278,  8928,  8558,  8130,  7788,  7419,  7140,
    6878,  6493,  6267,  5969,  5701,  5497,  5300,  5033,
    4831,  4578,  4397,  4232,  4053,  3857,  3723,  3514,
    3384,  3260,  3109,  2970,  2867,  2701,  2607,  2512,
    2384,  2290,  2206,  2078,  2006,  1935,  1830,  1764,
    1665,  1601,  1545,  1491,  1405,  1359,  1280,  1235,
    1190,  1129,  1081,  1045,   985,   951,   909,   866,
     834,   803,   758,   732,   706,   666,   644,   617,
     584,   564,   544,   512,   495,   466,   450,   434,
     410,   394,   380,   359,   347,   334,   315,   303,
     292,   277,   267,   257,   242,   234,   224,   213,
     203,   194,   184,   170,   158,   144,   129,   115,
     101,    86,    72,    58,    45,    35,    25,    16
};


/*
 * print_midi_msg
 * --------------
 * Displays midi messages as text.
 */
void print_midi_msg(unsigned int data, int n)
{
  if(!mod.log || !(mod.debug & (1<<DBG_MSG)))
    return;

  union
  {
    unsigned int data;
    unsigned char buff[4];
  } u;
  u.data = data;

  const char * const ctrl[] =
  {"bank hi","mod wheel hi","breath ctrl hi","(3)","foot pedal hi","portamento time hi",
   "data entry hi","volume hi","balance hi","(9)","pan posn hi","expression hi","effect ctrl 1 hi",
   "effect ctrl 2 hi","(14)","(15)","general ctrl 1 hi","general ctrl 2 hi",
   "general ctrl 3 hi","general ctrl 4 hi","(20)","(21)","(22)","(23)","(24)","(25)","(26)",
   "(27)","(28)","(29)","(30)","(31)","bank lo","mod wheel lo","breath ctrl lo","(35)",
   "foot pedal lo","portamento time lo","data entry lo","volume lo","balance lo","(41)",
   "pan posn lo","expression lo","effect ctrl 1 lo","effect ctrl 2 lo","(46)","(47)",
   "general slider 1 lo","general slider 2 lo","general slider 3 lo","general slider 4 lo",
   "(52)","(53)","(54)","(55)","(56)","(57)","(58)","(59)","(60)","(61)","(62)","(63)",
   "hold pedal","portamento","sustenuto pedal","soft pedal","legato pedal","hold 2 pedal",
   "sound variation","sound timbre","sound release time","sound attack time","sound brightness",
   "sound decay","sound vibrato rate","sound vibrato depth","sound vibrato delay","sound ctrl 10",
   "general ctrl 5","general ctrl 6","general ctrl 7","general ctrl 8","portamento ctrl","(85)","(86)",
   "(87)","(88)","(89)","(90)","effects level","tremulo level","chorus level","celeste level",
   "phaser level","data button inc","data button dec","non reg parm lo","non reg parm hi","reg parm lo",
   "reg parm hi","(102)","(103)","(104)","(105)","(106)","(107)","(108)","(109)","(110)","(111)",
   "(112)","(113)","(114)","(115)","(116)","(117)","(118)","(119)","all sound off",
   "all controllers off","local keyboard on","all notes off","omni mode off","omni mode on",
   "mono operation","poly operation"};

  const char * const sys[] =
  {"system exclusive","(1)","song position","song select","(4)","(5)","tune request","eox",
   "timing clock","(9)","start","continue","stop","(13)","active sensing","system reset"};

  const char * const rpn[] =
  {"pitch bend range","fine tuning","coarse tuning","tuning program","tuning bank","mod depth range"};

  const char offon[][4] = {"off","on"};

  int i;
  fprintf(mod.log, "[");
  for(i=0; i<n; i++)
    fprintf(mod.log, "%02X%s", u.buff[i], (i == (n-1)) ? "" : " ");
  fprintf(mod.log, "] ");

  int chan = (u.buff[0] & 0x0f) + 1;

  switch(u.buff[0] & 0xf0)
  {
    case NOTE_ON:       // 9x + 2 bytes, pitch (note), velocity
      if(u.buff[2] != 0)
      {
        fprintf(mod.log, "chan %d note on %s%d, %d", chan, note[u.buff[1] % 12], u.buff[1] / 12, u.buff[2]);
        break;
      } // follow through to note off
    case NOTE_OFF:      // 8x + 2 bytes, pitch (note), velocity
      fprintf(mod.log, "chan %d note off %s%d", chan, note[u.buff[1] % 12], u.buff[1] / 12);
      break;

    case KEY_PRESSURE:  // Ax + 2 bytes, pitch (note), pressure
      fprintf(mod.log, "chan %d key pressure %d %d", chan, u.buff[1], u.buff[2]);
      break;

    case CONTROL:       // Bx + 2 bytes, controller, setting
      if(((u.buff[1] == REG_PARM_lo) || (u.buff[1] == REG_PARM_hi)) && (u.buff[2] < 6))
        fprintf(mod.log, "chan %d %s %s", chan, ctrl[u.buff[1]], rpn[u.buff[2]]);

      else if(((u.buff[1] == REG_PARM_lo) || (u.buff[1] == REG_PARM_hi)) && (u.buff[2] == 0x7f))
        fprintf(mod.log, "chan %d %s end", chan, ctrl[u.buff[1]]);

      else if((u.buff[1] >= HOLD_PEDAL_on) && (u.buff[1] <= HOLD_2_PEDAL_on))
        fprintf(mod.log, "chan %d %s %s (%d)", chan, ctrl[u.buff[1]], offon[u.buff[2] >> 6], u.buff[2] & 0x3f);

      else
        fprintf(mod.log, "chan %d %s %d", chan, ctrl[u.buff[1]], u.buff[2]);
      break;

    case PROGRAM:       // Cx + 1 byte, program (patch)
      fprintf(mod.log, "chan %d program %d", chan, u.buff[1]);
      {
        if((u.buff[0] & 0xf) == (PERCUSSION_CHAN - 1))
        {
          if(syn.idat.kits[u.buff[1]])
            fprintf(mod.log, " percussion kit %d", u.buff[1]);
        }
        else
        {
          int i = syn.idat.bank[syn.idat.banks[0]-1].ins[u.buff[1]];
          if(i)
            fprintf(mod.log, " %s", syn.idat.instrument[i-1].name);
        }
      }
      break;

    case CHAN_PRESSURE: // Dx + 1 byte, pressure
      fprintf(mod.log, "chan %d pressure %d", chan, u.buff[1]);
      break;

    case PITCH_WHEEL:   // Ex + 2 bytes, 14bit value, 7 lsb's first
      fprintf(mod.log, "chan %d pitch wheel %04X", chan, u.buff[1] + (u.buff[2]<<7));
      break;

    case SYSTEM_EXCLUSIVE: // Fx
      fprintf(mod.log, "%s", sys[u.buff[0] & 0xf]);
      break;
  }

  fprintf(mod.log, "\n");
}



/*
 * iexp
 * ----
 * provides a 2 to the power x function
 * input Q10, output Q15
 */
extern const unsigned short int exp_table[1<<EXP_BITS]; // 0..1 in (Q10, 0..0x400), 1..2 out (Q15, 0x8000..0x10000)
int iexp(int x)
{
  int e = exp_table[x & ((1<<EXP_BITS)-1)];
  int i = x >> EXP_BITS;

  if(x >= 0)
    return e << i;
  else
    return e >> -i;
}


/*
 * calc_vol_expr
 * -------------
 * Calculates the actual channel volume from the provided
 * volume and expression values.
 * channel volume = (volume * expression) squared
 */
static void calc_vol_expr(chan_t *c)
{
  int vol = (c->volume * c->expression) >> MIDI_PARAM_SCALE;
  c->vol_expr = (vol * vol) >> MIDI_PARAM_SCALE;
}


/*
 * calc_pan_bal
 * ------------
 * Calculates the actual left and right channel volume from the provided
 * pan position and balance values.
 * Both pan and balance are midi 14 bit parameters having a range of 0 to 0x3fff.
 */
static void calc_pan_bal(chan_t *c)
{
  // first calculate left and right volumes due to pan positions
  int pan = c->pan_posn >> 7;
  int left = pantab[0x7f - pan];
  int right = pantab[pan];
  // then modify the volumes due to balance
  int bal = c->balance >> 7;
  c->left_vol = (left * pantab[0x7f - bal]) >> MIDI_PARAM_SCALE;
  c->right_vol = (right * pantab[bal]) >> MIDI_PARAM_SCALE;
}


/*
 * calc_pitch_bend
 * ---------------
 * Calculates the scaled pitch bend.
 * iexp i/p Q10 o/p Q15, pitch_wheel Q13, range cents, /8 = Q13 to Q10. >>2 = Q15 to Q13, /1200 = octave in cents
 */
static void calc_pitch_bend(chan_t *c)
{
  int range = 100 * (c->pitch_bend_range >> 7) + (c->pitch_bend_range & 0x7f); // in cents
  c->bender = iexp(((c->pitch_wheel - 0x2000) * range) / (8*1200)) >> 2; // scale and convert to linear
}


/*
 * calc_mod_wheel
 * --------------
 * mod_depth_range is in units of 100/128 cents
 * Other pitch mod parameters are vibrato_rate,
 */
static void calc_mod_wheel(chan_t *c)
{
  int range = (100 * c->mod_depth_range) >> 7; // in cents
  c->mod = iexp(((c->mod_wheel - 0x2000) * range) / (8*1200)) >> 2; // scale and convert to linear (incorrect)
}


/*
 * calc_glide
 * ----------
 * glide = 1 - (rate / 1000)   scaled to Q15 and by scale_factor
 * The 1000 is for sec to ms conversion. Formulae has been simplified in the code.
 */
void calc_glide(chan_t *c)
{
  int prt = port_rate_table[c->portamento_time >> 7]; // only use MSB
  int div = syn.scale_factor;

  c->glide = (prt >= (div * 1024)) ? 0 : 32768 - ((32 * prt) / div);
}


/*
 * select_rpn
 * ----------
 * Selects the registered parameter to control.
 */
static void select_rpn(chan_t *c)
{
  switch(c->param_idx)
  {
    case 0: c->param_ptr = &c->pitch_bend_range; break;
    case 5: c->param_ptr = &c->mod_depth_range; break;

    default: c->param_ptr = NULL;
  }
}


/*
 * reset_controllers
 * -----------------
 * Sets the controllers of a single channel to their default values.
 */
static void reset_controllers(chan_t *c)
{
  c->mod_wheel = def_chan.mod_wheel;
  c->expression = def_chan.expression;
  c->switches &= ~((1<<HOLD_PEDAL)|(1<<PORTAMENTO)|(1<<SUSTENUTO_PEDAL)|(1<<SOFT_PEDAL));
  c->switches_delta = 0;
  c->param_ptr = NULL;
  c->param_idx = 0x3fff;
  c->pitch_wheel = def_chan.pitch_wheel;
  c->pressure = def_chan.pressure;
}


/*
 * interpret_sys_ex
 * ----------------
 * Interprets the system exclusive message buffer. The first character of the sysex
 * message (0xf0) is not in the buffer. Sysex device ID is ignored.
 */
static void interpret_sys_ex(unsigned char *msg)
{
  int n;

//  if(msg[1] != 0x7f) return; // device ID (ignore)

  switch(msg[0]) // ID
  {
    case REAL_TIME_ID:
      switch(msg[2]) // Sub-ID1
      {
        case 4: // device control
          switch(msg[3]) // Sub-ID2
          {
            case 1: // master volume
              syn.master_volume = (msg[4] | (msg[5] << 7)) >> 4; // scaled for 10 bit
              if(mod.debug & (1<<DBG_MSG))
                if(mod.log)fprintf(mod.log, "master volume = %d\n", syn.master_volume);
              break;

            case 2: // master balance
              syn.master_balance = (msg[4] | (msg[5] << 7)) >> 4; // scaled for 10 bit
              if(mod.debug & (1<<DBG_MSG))
                if(mod.log)fprintf(mod.log, "master balance = %d\n", syn.master_balance);
              break;

            // master tuning as a single number is a signed binary number in semitones with 13 fractional bits
            // range is +-64 semitones
            // note. syn.master_tuning is currently not used, the coarse and fine parts of it are used separately.

            case 3: // master fine tuning, +- 1 semitone, the fractional part
              n = (msg[4] | (msg[5] << 7)) - 0x2000;
              syn.master_fine = n;
              syn.master_tuning = (syn.master_coarse << 13) + n;
              if(mod.debug & (1<<DBG_MSG))
                if(mod.log)fprintf(mod.log, "master fine tuning = %d\n", syn.master_fine);
              break;

            case 4: // master coarse tuning, lsb unused, 100 cents (1 semitone) per step, the integer part
              n = msg[5] - 0x40;
              syn.master_coarse = n;
              syn.master_tuning = syn.master_fine + (n << 13);
              if(mod.debug & (1<<DBG_MSG))
                if(mod.log)fprintf(mod.log, "master coarse tuning = %d\n", syn.master_coarse);
              break;

          }
          break;
      }
      break;

    case NON_REAL_TIME_ID:
      switch(msg[2]) // Sub-ID1
      {
        case 9: // GM control
          switch(msg[3]) // Sub-ID2
          {
            case 1: // GM1 system on
              midiSynth_reset();
              if(mod.debug & (1<<DBG_MSG))
                if(mod.log)fprintf(mod.log, "GM Reset\n");
              break;

//            case 2: // GM system off
//            case 3: // GM2 system on
          }
          break;
      }
      break;
  }
}


/*
 * load_14bit_value
 * ----------------
 * Midi data is only 7 bits per byte. This accumulates 2 bytes into a 14 bit value.
 * Data is sent lsb first, msb second, as determined by the data count.  It doesn't
 * actually matter which order the data comes, and sometimes only the MSB is sent.
 */
static void load_14bit_value(unsigned short *param, int data, int byte)
{
  if(!param) // unassigned
    return;

  if(byte == LSB)
    *param = (*param & ~0x7f) | data;
  else // msb
    *param = (*param & ~(0x7f << 7)) | (data << 7);
}


/*
 * midiSynth_midi_in
 * -----------------
 * This is the main synth remote control input, it interprets data from midi input ports.
 * Most changes to synth parameters will be picked up automatically by the regular synth
 * task and audio output interrupt.
 */
void midiSynth_midi_in(int data)
{
  int real_time, on;
  chan_t *c;

  if (data & 0x80)
  {
    // status byte

    real_time = 0;

    if (data < 0xf0)
    {
      // voice messages
      syn.m_in.type    = data & 0xf0;
      syn.m_in.channel = data & 0x0f;

      // set message lengths
      switch (syn.m_in.type)
      {
        case NOTE_OFF:
        case NOTE_ON:
        case KEY_PRESSURE:
        case CONTROL:
        case PITCH_WHEEL:
          syn.m_in.count = 2;
          break;

        case PROGRAM:
        case CHAN_PRESSURE:
          syn.m_in.count = 1;
          break;
      }
    }
    else
    {
      // system messages
      switch (data)
      {
        // system common commands

        case SYSTEM_EXCLUSIVE: // variable length until terminated by an EOX or any status byte
          syn.m_in.type = data;
          syn.sysex.ix = 0;
          break;

        case SONG_POSITION:
          syn.m_in.type = data;
          syn.m_in.count = 2;
          break;

        case SONG_SELECT:
          syn.m_in.type = data;
          syn.m_in.count = 1;
          break;

        case TUNE_REQUEST:
          // no associated data
          break;

        case EOX: // system exclusive terminator
          // no need to do anything here, it will be picked up after the switch
          break;

        // real time (no associated data)
        case TIMING_CLOCK:
        case START:
        case CONTINUE:
        case STOP:
        case ACTIVE_SENSING:
          real_time = 1;
          break;

        case SYSTEM_RESET:
          real_time = 1;
          midiSynth_reset(); // reset synth
          break;
      }
    }

    // check for an unterminated system exclusive message
    if(!real_time && (syn.sysex.ix > 0))
    {
      syn.sysex.buff[syn.sysex.ix] = EOX;
      interpret_sys_ex(syn.sysex.buff);
      syn.sysex.ix = 0;
    }
  }
  else
  {
    // data byte
    c = &syn.chan[syn.m_in.channel]; // point to the current channel (not always needed)
    on = data >> 6; // for on/off controls (not always needed)

    switch (syn.m_in.type)
    {
      case SYSTEM_EXCLUSIVE:
        if(syn.sysex.ix < SYS_LEN) // discard data if the buffer is full
          syn.sysex.buff[syn.sysex.ix++] = data;
        break;

      case SONG_SELECT:
        syn.m_in.song = data;
        break;

      case SONG_POSITION:
        load_14bit_value(&syn.m_in.position, data, syn.m_in.count);
        break;

      case NOTE_OFF:
        if(syn.m_in.count == 2) // pitch
          syn.m_in.pitch = data;
        else // velocity
        {
          syn.m_in.velocity = data;
          poly_action_key(syn.m_in.pitch, FALSE, syn.m_in.channel, syn.m_in.velocity);
        }
        break;

      case NOTE_ON:
        if(syn.m_in.count == 2) // pitch
          syn.m_in.pitch = data;
        else // velocity
        {
          syn.m_in.velocity = data;
          poly_action_key(syn.m_in.pitch, (data != 0), syn.m_in.channel, syn.m_in.velocity); // note off if data == 0
        }
        break;

      case KEY_PRESSURE:
        if(syn.m_in.count == 2) // pitch
          syn.m_in.pitch = data;
        else // pressure
          syn.m_in.pressure = data;
        break;

      case CONTROL:
        if(syn.m_in.count == 2)
        {
          syn.m_in.controller = data; // controller number
          // channel mode messages with no associated data
          switch(syn.m_in.controller)
          {
            case ALL_SOUND_OFF:
              reset_gates(syn.m_in.channel);
              break;

            case ALL_CONTROLLERS_OFF:
              reset_controllers(c);
              break;

            case LOCAL_KEYBOARD_on:
              break;

            case ALL_NOTES_OFF:
              reset_gates(syn.m_in.channel);
              break;

            case OMNI_MODE_OFF:
              syn.switches |= (1<<OMNI_MODE);
              break;

            case OMNI_MODE_ON:
              syn.switches &= ~(1<<OMNI_MODE);
              break;

            case POLY_OPERATION:
              syn.switches &= ~(1<<POLY_MODE);
              break;
          }
        }
        else // setting
        {
          int hi_lo = (syn.m_in.controller >> 5) + 1; // 1=MSB, 2=LSB
          switch (syn.m_in.controller)
          {
#ifdef PROGRAM_UPDATES_BANKS
            case BANK_hi: c->temp.hi = data; break;
            case BANK_lo: c->temp.lo = data; break;
#else
            case BANK_hi: c->patch.hi = data; break;
            case BANK_lo: c->patch.lo = data; break;
#endif

            case MOD_WHEEL_hi:
            case MOD_WHEEL_lo:
              load_14bit_value(&c->mod_wheel, data, hi_lo);
              calc_mod_wheel(c);
              break;

            case BREATH_CTRL_hi:
            case BREATH_CTRL_lo:
              load_14bit_value(&c->breath_ctrl, data, hi_lo);
              break;

            case FOOT_PEDAL_hi:
            case FOOT_PEDAL_lo:
              load_14bit_value(&c->foot_pedal, data, hi_lo);
              break;

            case PORTAMENTO_TIME_hi:
            case PORTAMENTO_TIME_lo:
              load_14bit_value(&c->portamento_time, data, hi_lo);
              calc_glide(c);
              break;

            case DATA_ENTRY_hi:
            case DATA_ENTRY_lo:
              load_14bit_value(c->param_ptr, data, hi_lo);
              break;

            case VOLUME_hi:
            case VOLUME_lo:
              load_14bit_value(&c->volume, data, hi_lo);
              calc_vol_expr(c);
              break;

            case BALANCE_hi:
            case BALANCE_lo:
              load_14bit_value(&c->balance, data, hi_lo);
              calc_pan_bal(c);
              break;

            case PAN_POSN_hi:
            case PAN_POSN_lo:
              load_14bit_value(&c->pan_posn, data, hi_lo);
              calc_pan_bal(c);
              break;

            case EXPRESSION_hi:
            case EXPRESSION_lo:
              load_14bit_value(&c->expression, data, hi_lo);
              calc_vol_expr(c);
              break;

            case PORTAMENTO_CTRL:
              c->portamento_ctrl = data;
              if(data > 0)
                c->switches |= (1<<PORTAM_CTRL);
              break;

            case SOUND_VIBRATO_RATE:  c->vibrato_rate  = data; break;
            case SOUND_VIBRATO_DEPTH: c->vibrato_depth = data; break;
            case SOUND_VIBRATO_DELAY: c->vibrato_delay = data; break;

            case HOLD_PEDAL_on:
              c->switches = (c->switches & ~(1<<HOLD_PEDAL)) | (on << HOLD_PEDAL);
              c->switches_delta |= (1<<HOLD_PEDAL);
              break;

            case PORTAMENTO_on:
              c->switches = (c->switches & ~(1<<PORTAMENTO)) | (on << PORTAMENTO);
              c->switches_delta |= (1<<PORTAMENTO);
              portamento_on_off(syn.m_in.channel, on);
              break;

            case SUSTENUTO_PEDAL_on:
              c->switches = (c->switches & ~(1<<SUSTENUTO_PEDAL)) | (on << SUSTENUTO_PEDAL);
              c->switches_delta |= (1<<SUSTENUTO_PEDAL);
              break;

            case SOFT_PEDAL_on:
              c->switches = (c->switches & ~(1<<SOFT_PEDAL)) | (on << SOFT_PEDAL);
              c->switches_delta |= (1<<SOFT_PEDAL);
              break;

            case LEGATO_PEDAL_on:
              c->switches = (c->switches & ~(1<<LEGATO_PEDAL)) | (on << LEGATO_PEDAL);
              c->switches_delta |= (1<<LEGATO_PEDAL);
              break;

            case HOLD_2_PEDAL_on:
              c->switches = (c->switches & ~(1<<HOLD_2_PEDAL)) | (on << HOLD_2_PEDAL);
              c->switches_delta |= (1<<HOLD_2_PEDAL);
              break;

            case DATA_BUTTON_INC: if(c->param_ptr)(*c->param_ptr)++; break;
            case DATA_BUTTON_DEC: if(c->param_ptr)(*c->param_ptr)--; break;

            case REG_PARM_hi:
            case NON_REG_PARM_hi: load_14bit_value(&c->param_idx, data, MSB); select_rpn(c); break;

            case REG_PARM_lo:
            case NON_REG_PARM_lo: load_14bit_value(&c->param_idx, data, LSB); select_rpn(c); break;

            case MONO_OPERATION:
              // data = number of monophonic voices
              syn.switches = (syn.switches & ~(MONO_MASK<<MONO_VOICES)) | (1<<POLY_MODE) | (data<<MONO_VOICES);
              break;
          }
        }
        break;

      case PROGRAM:
        c->patch.prg = data;
#ifdef PROGRAM_UPDATES_BANKS
        c->patch.hi = c->temp.hi;
        c->patch.lo = c->temp.lo;
#endif
        break;

      case CHAN_PRESSURE:
        c->pressure = data;
        break;

      case PITCH_WHEEL:
        load_14bit_value(&c->pitch_wheel, data, syn.m_in.count);
        calc_pitch_bend(c);
        break;
    }

    syn.m_in.count ^= 3; // toggle between 1 and 2 (MSB and LSB for 14 bit values)
  }
}


