/*
  !MidiPlay   A MIDI synthesiser and file player.

  midi.c - MIDI functions.

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

#include "main.h"
#include "kbd.h"
#include "player.h"
#include "midisyn.h"
#include "kbd_dsplay.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 * const note[] = {"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 int scale_factor = 100;
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
};

void reset_sleep_timer(void); // ro_audio.c

/*
 * print_midi_msg
 * --------------
 * Displays midi messages as text. Used by all sources of midi messages and is usually
 * enabled by a debug option.
 */
void print_midi_msg(int source, int status, unsigned char *buff, int n)
{
  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 src[] =
  {"Player","Keyboard","Editor"};

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

  myprintf("%s [%02X ", src[source], status);

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

  if((buff[0] & 0xf0) != 0xf0)
    myprintf(" chan %d ", (status & (NUM_MIDI_CHANS - 1)) + 1);

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

    case KEY_PRESSURE:  // Ax + 2 bytes, pitch (note), pressure
      myprintf("key pressure %d %d", buff[0], buff[1]);
      break;

    case CONTROL:       // Bx + 2 bytes, controller, setting
      if(((buff[0] == REG_PARM_lo) || (buff[0] == REG_PARM_hi)) && (buff[1] < 6))
        myprintf("%s %s", ctrl[buff[0]], rpn[buff[1]]);

      else if(((buff[0] == REG_PARM_lo) || (buff[0] == REG_PARM_hi)) && (buff[1] == 0x7f))
        myprintf("%s end", ctrl[buff[0]]);

      else if((buff[0] >= HOLD_PEDAL_on) && (buff[0] <= HOLD_2_PEDAL_on))
        myprintf("%s %s (%d)", ctrl[buff[0]], offon[buff[1] >> 6], buff[1] & 0x3f);

      else
        myprintf("%s %d", ctrl[buff[0]], buff[1]);
      break;

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

    case CHAN_PRESSURE: // Dx + 1 byte, pressure
      myprintf("channel pressure %d", buff[0]);
      break;

    case PITCH_WHEEL:   // Ex + 2 bytes, 14bit value, 7 lsb's first
      myprintf("pitch wheel %04X", buff[0] + (buff[1]<<7));
      break;

    case SYSTEM_EXCLUSIVE: // Fx
      myprintf("%s", sys[status & 0xf]);
      break;
  }

  myprintf("\n");
}


/*
 * cmd_glide
 * ---------
 * Sets, displays, and stores, the glide scaling factor
 */
int cmd_glide(char *msg, int len)
{
  unsigned int value;

  if(msg[0] == '?')
    myprintf(" Sets and displays the glide scaling factor.\n"
           "  ,<value>  Set the scaling factor to the value\n"
           "            min 0 (fastest), max 1000 (slowest)\n");

  else if(len == 0) // display status
    myprintf("Glide factor = %d\n", scale_factor);

  else if(msg[0] == '!') // report for controller
    myprintf("GLIDE,%d\n", scale_factor);

  else if(sscanf(msg+1, "%d", &value) != 1)
    return -PARAMETER_ERROR;

  else if((value < 0) || (value > 1000))
    return -OUT_OF_RANGE;

  else
    scale_factor = value;

  return NO_ERROR_;
}


/*
 * 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;

  int data[2];
  data[0] = syn.m_in.channel;
  data[1] = c->vol_expr;
  midiPlayer_display(ITEM_CHN_VOL, data);

  if(mpg.debug & (1<<DEBUG_MIDI2))
    myprintf("ch %d, vol %04X expr %04X combined %04X\n", syn.m_in.channel+1, c->volume, c->expression, c->vol_expr);
}


/*
 * 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;

  if(mpg.debug & (1<<DEBUG_MIDI2))
    myprintf("ch %d, pan %04X bal %04X left %04X right %04X\n", syn.m_in.channel+1, c->pan_posn, c->balance, c->left_vol, c->right_vol);
}


/*
 * 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

  if(mpg.debug & (1<<DEBUG_MIDI2))
    myprintf("ch %d, bend %X, wheel %X, range %d\n", syn.m_in.channel+1, c->bender, c->pitch_wheel, c->pitch_bend_range >> 7);
}


/*
 * 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)

  if(mpg.debug & (1<<DEBUG_MIDI2))
    myprintf("ch %d, mod %X, wheel %X, range %d\n", syn.m_in.channel+1, c->mod, c->mod_wheel, c->mod_depth_range >> 7);
}


/*
 * 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 = 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.
 */
unsigned short gm_control;
static void interpret_sys_ex(unsigned char *msg)
{
  // Master Volume
  if((msg[0] == REAL_TIME_ID) &&
//   (msg[1] == 0x7f) &&          SysEx channel, ignore
     (msg[2] == 4) &&          // Sub-ID, device control
     (msg[3] == 1))            // Sub-ID2, master volume
    syn.master_volume = (msg[4] | (msg[5] << 7)) >> 4; // scaled for 10 bit

  // GM system control
  else if((msg[0] == NON_REAL_TIME_ID) &&
//   (msg[1] == 0x7f) &&          SysEx channel, ignore
     (msg[2] == 9))            // Sub-ID, GM control
  {
    gm_control = msg[3] & 1;   // 0 = off, 1 = on

    if(gm_control == 1)
      midiSynth_reset(); // reset synth
  }
}


/*
 * 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;

  reset_sleep_timer();

  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 STOP:
        case CONTINUE:
        case ACTIVE_SENSING:
        case SYSTEM_RESET:
          real_time = 1;
          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);
          highlight_midi_key(syn.m_in.pitch, FALSE, syn.m_in.channel);
        }
        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
          highlight_midi_key(syn.m_in.pitch, (data != 0), syn.m_in.channel);
        }
        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);
              if(mpg.debug & (1<<DEBUG_GLIDE))
                myprintf("ch %d portamento time %02X:%02X, glide %04X\n",
                       syn.m_in.channel+1, c->portamento_time >> 7, c->portamento_time & 0x7f, c->glide);
              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);
              if(mpg.debug & (1<<DEBUG_GLIDE))
                myprintf("ch %d portamento ctrl %d\n", syn.m_in.channel+1, data);
              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);
              if(mpg.debug & (1<<DEBUG_MIDI2))
                myprintf("ch %d mono %d voices\n", syn.m_in.channel, data);
              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
//printf("prg %d\n",data);
        {
          int data[3];
          int prg = c->patch.prg;
          data[0] = syn.m_in.channel;
          data[1] = prg;
          if(syn.m_in.channel == (PERCUSSION_CHAN-1))
            data[2] = (int)(syn.idat.bank[syn.idat.kits[0]-1].name); // show GM drum kit
          else
            data[2] = (int)(syn.idat.instrument[syn.idat.bank[syn.idat.banks[0]-1].ins[prg]-1].name); // show GM instruments
          midiPlayer_display(ITEM_CHN_PRG, data);
        }
        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)
  }
}


