/*
editor.c
--------

Synth instrument editor
13/6/22
*/

#include "wimp.h"
#include "lib.h"
#include "ro_main.h"
#include "main.h"
#include "kbd.h"
#include "midisyn.h"
#include "kbd_dsplay.h"


/*
 * open_editor
 * -----------
 */
void open_editor(void)
{
  _kernel_swi_regs regs;
  int blk[10];

  blk[0] = ro.handle[WIN_EDT];
  regs.r[1] = (int) blk;
  _kernel_swi(Wimp_GetWindowState, &regs, &regs);
  _kernel_swi(Wimp_OpenWindow, &regs, &regs);
  _kernel_swi(Wimp_GetWindowState, &regs, &regs);
  blk[0] = ro.cur_pane;
  blk[7] = -1; // bring to front
  _kernel_swi(Wimp_OpenWindow, &regs, &regs);
  blk[0] = ro.handle[WIN_EDT];
  blk[7] = ro.cur_pane;
  _kernel_swi(Wimp_OpenWindow, &regs, &regs);
}


/*
 * close_editor
 * ------------
 */
void close_editor(void)
{
  _kernel_swi_regs regs;
  int blk[10];
  regs.r[1] = (int)blk;

  if (ro.cur_pane != -1)
  {
    blk[0] = ro.cur_pane;
    _kernel_swi(Wimp_CloseWindow, &regs, &regs);
  }

  blk[0] = ro.handle[WIN_EDT];
  _kernel_swi(Wimp_CloseWindow, &regs, &regs);
}


/*
 * set_editor_pane
 * ---------------
 */
void set_editor_pane(int icon)
{
  int pane;

  switch(icon)
  {
    case ICON_ED_WAVE1:   pane = ro.handle[WIN_ED_WAVE1];   break;
    case ICON_ED_WAVE2:   pane = ro.handle[WIN_ED_WAVE2];   break;
    case ICON_ED_FILTER:  pane = ro.handle[WIN_ED_FILTER];  break;
    case ICON_ED_NOISE:   pane = ro.handle[WIN_ED_NOISE];   break;
    case ICON_ED_GENERAL: pane = ro.handle[WIN_ED_GENERAL]; break;
    default: return;
  }

  if (ro.cur_pane == pane)
    return;

  _kernel_swi_regs regs;
  int blk[10];
  regs.r[1] = (int)blk;

  if (ro.cur_pane != -1)
  {
    blk[0] = ro.cur_pane;
    _kernel_swi(Wimp_CloseWindow, &regs, &regs);

    ro.cur_pane = pane;
    open_editor();
  }

  ro.cur_pane = pane;
}


/*
 * kit_check
 * ---------
 * If the currently selected instrument for editing is a drum kit,
 * all editing buttons in the editor window are greyed out,
 * otherwise they are enabled.
 */
static void kit_check(void)
{
  int state = (midiPlayer_status() >> 5) & 1; // true if current instrument is a drum kit (not editable)
  int win = ro.handle[WIN_EDT];
  icon_disabled_change(state, win, ICON_ED_RELOAD);
  icon_disabled_change(state, win, ICON_ED_CLEAR);
  icon_disabled_change(state, win, ICON_ED_DELETE);
  icon_disabled_change(state, win, ICON_ED_SWAP);
  icon_disabled_change(state, win, ICON_ED_SAVE);
  icon_disabled_change(state, win, ICON_ED_SAVE_NEW);
  icon_disabled_change(state, win, ICON_ED_INST_NAME);
  icon_disabled_change(state, win, ICON_ED_BANK_NAME);
}


/*
 * edt_slider_control
 * ------------------
 * blk = block from Wimp_GetPointerInfo
 * Returns True if dealt with here, else False
 */
int edt_slider_control(int *blk)
{
  if(ro.slider.window == ro.handle[WIN_ED_WAVE1]) // editor wave1 pane
    switch(ro.slider.icon)
    {
      case ICON_ENV_DY_SLDR: midiPlayer_cmds("USER,4,%d", trb_to_param(SLDR_ENV_DY, slider_get_posn(blk, SLDR_ENV_DY))); break; // envelope delay
      case ICON_ENV_AR_SLDR: midiPlayer_cmds("USER,5,%d", trb_to_param(SLDR_ENV_AR, slider_get_posn(blk, SLDR_ENV_AR))); break; // envelope attack step
      case ICON_ENV_AT_SLDR: midiPlayer_cmds("USER,6,%d", trb_to_param(SLDR_ENV_AT, slider_get_posn(blk, SLDR_ENV_AT))); break; // envelope attack target
      case ICON_ENV_AH_SLDR: midiPlayer_cmds("USER,7,%d", trb_to_param(SLDR_ENV_AH, slider_get_posn(blk, SLDR_ENV_AH))); break; // envelope decay rate
      case ICON_ENV_DR_SLDR: midiPlayer_cmds("USER,8,%d", trb_to_param(SLDR_ENV_DR, slider_get_posn(blk, SLDR_ENV_DR))); break; // envelope decay rate
      case ICON_ENV_DT_SLDR: midiPlayer_cmds("USER,9,%d", trb_to_param(SLDR_ENV_DT, slider_get_posn(blk, SLDR_ENV_DT))); break; // envelope decay target
      case ICON_ENV_SR_SLDR: midiPlayer_cmds("USER,10,%d", trb_to_param(SLDR_ENV_SR, slider_get_posn(blk, SLDR_ENV_SR))); break; // envelope sustain rate
      case ICON_ENV_RR_SLDR: midiPlayer_cmds("USER,11,%d", trb_to_param(SLDR_ENV_RR, slider_get_posn(blk, SLDR_ENV_RR))); break; // envelope release rate
      case ICON_WAV_GT_SLDR: midiPlayer_cmds("USER,3,%d", trb_to_param(SLDR_GLIDE, slider_get_posn(blk, SLDR_GLIDE))); break; // glide time
    }
  else if(ro.slider.window == ro.handle[WIN_ED_WAVE2]) // editor wave2 pane
    switch(ro.slider.icon)
    {
      case ICON_ENV_DY_SLDR: midiPlayer_cmds("USER,16,%d", trb_to_param(SLDR_ENV_DY, slider_get_posn(blk, SLDR_ENV_DY))); break; // envelope delay
      case ICON_ENV_AR_SLDR: midiPlayer_cmds("USER,17,%d", trb_to_param(SLDR_ENV_AR, slider_get_posn(blk, SLDR_ENV_AR))); break; // envelope attack step
      case ICON_ENV_AT_SLDR: midiPlayer_cmds("USER,18,%d", trb_to_param(SLDR_ENV_AT, slider_get_posn(blk, SLDR_ENV_AT))); break; // envelope attack target
      case ICON_ENV_AH_SLDR: midiPlayer_cmds("USER,19,%d", trb_to_param(SLDR_ENV_AH, slider_get_posn(blk, SLDR_ENV_AH))); break; // envelope decay rate
      case ICON_ENV_DR_SLDR: midiPlayer_cmds("USER,20,%d", trb_to_param(SLDR_ENV_DR, slider_get_posn(blk, SLDR_ENV_DR))); break; // envelope decay rate
      case ICON_ENV_DT_SLDR: midiPlayer_cmds("USER,21,%d", trb_to_param(SLDR_ENV_DT, slider_get_posn(blk, SLDR_ENV_DT))); break; // envelope decay target
      case ICON_ENV_SR_SLDR: midiPlayer_cmds("USER,22,%d", trb_to_param(SLDR_ENV_SR, slider_get_posn(blk, SLDR_ENV_SR))); break; // envelope sustain rate
      case ICON_ENV_RR_SLDR: midiPlayer_cmds("USER,23,%d", trb_to_param(SLDR_ENV_RR, slider_get_posn(blk, SLDR_ENV_RR))); break; // envelope release rate
      case ICON_WAV_GT_SLDR: midiPlayer_cmds("USER,15,%d", trb_to_param(SLDR_GLIDE, slider_get_posn(blk, SLDR_GLIDE))); break; // glide time
    }
  else if(ro.slider.window == ro.handle[WIN_ED_FILTER]) // editor filter pane
    switch(ro.slider.icon)
    {
      case ICON_ENV_DY_SLDR: midiPlayer_cmds("USER,32,%d", trb_to_param(SLDR_ENV_DY, slider_get_posn(blk, SLDR_ENV_DY))); break; // envelope delay
      case ICON_ENV_AR_SLDR: midiPlayer_cmds("USER,33,%d", trb_to_param(SLDR_ENV_AR, slider_get_posn(blk, SLDR_ENV_AR))); break; // envelope attack step
      case ICON_ENV_AT_SLDR: midiPlayer_cmds("USER,34,%d", trb_to_param(SLDR_ENV_AT, slider_get_posn(blk, SLDR_ENV_AT))); break; // envelope attack target
      case ICON_ENV_AH_SLDR: midiPlayer_cmds("USER,35,%d", trb_to_param(SLDR_ENV_AH, slider_get_posn(blk, SLDR_ENV_AH))); break; // envelope decay rate
      case ICON_ENV_DR_SLDR: midiPlayer_cmds("USER,36,%d", trb_to_param(SLDR_ENV_DR, slider_get_posn(blk, SLDR_ENV_DR))); break; // envelope decay rate
      case ICON_ENV_DT_SLDR: midiPlayer_cmds("USER,37,%d", trb_to_param(SLDR_ENV_DT, slider_get_posn(blk, SLDR_ENV_DT))); break; // envelope decay target
      case ICON_ENV_SR_SLDR: midiPlayer_cmds("USER,38,%d", trb_to_param(SLDR_ENV_SR, slider_get_posn(blk, SLDR_ENV_SR))); break; // envelope sustain rate
      case ICON_ENV_RR_SLDR: midiPlayer_cmds("USER,39,%d", trb_to_param(SLDR_ENV_RR, slider_get_posn(blk, SLDR_ENV_RR))); break; // envelope release rate
      case ICON_FLT_RES_SLDR: midiPlayer_cmds("USER,42,%d", trb_to_param(SLDR_RESONANCE, slider_get_posn(blk, SLDR_RESONANCE))); break; // resonance
    }
  else if(ro.slider.window == ro.handle[WIN_ED_NOISE]) // editor noise pane
    switch(ro.slider.icon)
    {
      case ICON_ENV_DY_SLDR: midiPlayer_cmds("USER,24,%d", trb_to_param(SLDR_ENV_DY, slider_get_posn(blk, SLDR_ENV_DY))); break; // envelope delay
      case ICON_ENV_AR_SLDR: midiPlayer_cmds("USER,25,%d", trb_to_param(SLDR_ENV_AR, slider_get_posn(blk, SLDR_ENV_AR))); break; // envelope attack step
      case ICON_ENV_AT_SLDR: midiPlayer_cmds("USER,26,%d", trb_to_param(SLDR_ENV_AT, slider_get_posn(blk, SLDR_ENV_AT))); break; // envelope attack target
      case ICON_ENV_AH_SLDR: midiPlayer_cmds("USER,27,%d", trb_to_param(SLDR_ENV_AH, slider_get_posn(blk, SLDR_ENV_AH))); break; // envelope decay rate
      case ICON_ENV_DR_SLDR: midiPlayer_cmds("USER,28,%d", trb_to_param(SLDR_ENV_DR, slider_get_posn(blk, SLDR_ENV_DR))); break; // envelope decay rate
      case ICON_ENV_DT_SLDR: midiPlayer_cmds("USER,29,%d", trb_to_param(SLDR_ENV_DT, slider_get_posn(blk, SLDR_ENV_DT))); break; // envelope decay target
      case ICON_ENV_SR_SLDR: midiPlayer_cmds("USER,30,%d", trb_to_param(SLDR_ENV_SR, slider_get_posn(blk, SLDR_ENV_SR))); break; // envelope sustain rate
      case ICON_ENV_RR_SLDR: midiPlayer_cmds("USER,31,%d", trb_to_param(SLDR_ENV_RR, slider_get_posn(blk, SLDR_ENV_RR))); break; // envelope release rate
    }
  else if(ro.slider.window == ro.handle[WIN_ED_GENERAL]) // editor general pane
    switch(ro.slider.icon)
    {
      case ICON_COM_FM_SLDR: midiPlayer_cmds("USER,47,%d", trb_to_param(SLDR_FM, slider_get_posn(blk, SLDR_FM))); break; // FM Mod Depth
      case ICON_COM_DT_SLDR: midiPlayer_cmds("USER,48,%d", trb_to_param(SLDR_DETUNE, slider_get_posn(blk, SLDR_DETUNE))); break; // Wave 1/2 Detune
      case ICON_MOD_RATE_SLDR: midiPlayer_cmds("USER,45,%d", trb_to_param(SLDR_MOD_RATE, slider_get_posn(blk, SLDR_MOD_RATE))); break; // Modulation rate
      case ICON_MOD_DPTH_SLDR: midiPlayer_cmds("USER,46,%d", trb_to_param(SLDR_MOD_DEPTH, slider_get_posn(blk, SLDR_MOD_DEPTH))); break; // Modulation Depth
      case ICON_GEN_ERT_SLDR: midiPlayer_cmds("USER,43,%d", trb_to_param(SLDR_GEN_RETRIG, slider_get_posn(blk, SLDR_GEN_RETRIG))); break; // Envelope Retrigger Time
      case ICON_GEN_GAIN_SLDR: midiPlayer_cmds("USER,49,%d", trb_to_param(SLDR_GEN_GAIN, slider_get_posn(blk, SLDR_GEN_GAIN))); break; // Instrument Overall Gain
    }

  else
    return 0;

  return 1;
}


/*
 * edt_mouse_click
 * ---------------
 * blk = block from Wimp_Poll
 * state = icon state, selected or not
 * Returns True if dealt with here, else False
 */
int edt_mouse_click(int *blk, int state)
{
  static int confirmed;   // true when a deletion has been confirmed
  static int swapped;     // true when temp is loaded from syn.user;
  static int first_valid; // true when first instrument of a pair to swap has been entered
  static int swap_instr;  // first instrument of the pair to be swapped
  _kernel_swi_regs regs;

  int ret = 1;

  // Editor window
  if(blk[3] == ro.handle[WIN_EDT])
  {
    // first get current instrument number
    int b[10];
    b[0] = blk[3]; // window
    b[1] = ICON_ED_PATCH; // icon
    regs.r[1] = (int)b;
    _kernel_swi(Wimp_GetIconState, &regs, &regs);
    int cur = atoi((char *)(b[7]));

    if(blk[2] == MOUSE_SELECT)
      switch(blk[4])
      {
        case ICON_ED_WAVE1:
        case ICON_ED_WAVE2:
        case ICON_ED_FILTER:
        case ICON_ED_NOISE:
        case ICON_ED_GENERAL:
          set_editor_pane(blk[4]);
          break;

        case ICON_ED_PATCH_DEC: midiPlayer_cmds("INSTRUMENT,-"); kit_check(); break; // previous instrument
        case ICON_ED_PATCH_INC: midiPlayer_cmds("INSTRUMENT,+"); kit_check(); break; // next instrument

        case ICON_ED_RELOAD:
          //if(syn.kbd_patch.list[syn.kbd_patch.cur].hi == PERCUSSION_BANK)
          //  err = -NOT_EDITABLE; // drum kit
          //else
          if(swapped)
          {
            midiPlayer_cmds("STORE,U\n" // syn.user = temp
                            "USER,999,0"); // redisplay syn.user
          }
          else
          {
            midiPlayer_cmds("STORE,T\n" // temp = syn.user
                            "INSTRUMENT,L,%d", cur); // syn.user = syn.kbd_patch.cur
          }
          swapped ^= 1;
          icon_text_change((swapped) ? "Saved in 'temp'" : "", blk[3], ICON_ED_MESSAGE);
          break;

        case ICON_ED_CLEAR:
          //if(syn.kbd_patch.list[syn.kbd_patch.cur].hi == PERCUSSION_BANK)
          //  err = -NOT_EDITABLE; // drum kit
          //else
          midiPlayer_cmds("STORE,C"); // syn.user = 0
          break;

        case ICON_ED_DELETE:
          if(confirmed)
            midiPlayer_cmds("STORE,D"); // syn.kbd_patch.cur = syn.user = 0
          confirmed ^= 1;
          icon_text_change((confirmed) ? "Press again to confirm" : "", blk[3], ICON_ED_MESSAGE);
          break;

        case ICON_ED_SWAP:
          if(!first_valid)
            swap_instr = cur;
          else
            midiPlayer_cmds("STORE,W,%d", swap_instr); // syn.kbd_patch.cur <-> swap_instr
          first_valid ^= 1;
          icon_text_change((first_valid) ? "Swap with ?" : "", blk[3], ICON_ED_MESSAGE);
          break;

        case ICON_ED_SAVE:
          midiPlayer_cmds("STORE,S"); // syn.kbd_patch.cur = syn.user
          break;

        case ICON_ED_SAVE_NEW:
          midiPlayer_cmds("STORE,V"); // new variation = syn.user;
          break;

        case ICON_ED_BACKUP:
          midiPlayer_cmds("STORE,F,"APP_DIR".SoundSet\n"   // backup data
                          "STORE,I,"APP_DIR".SoundS"); // backup source,text,csv (filename is limited to 6 characters)
          break;
      }
  }

  // Editor wave1 pane
  else if(blk[3] == ro.handle[WIN_ED_WAVE1])
  {
    if(blk[2] == MOUSE_SELECT)
      switch(blk[4])
      {
        case ICON_ENV_DY_BACK: // Delay
        case ICON_ENV_AR_BACK: // Attack Rate
        case ICON_ENV_AT_BACK: // Attack Target
        case ICON_ENV_AH_BACK: // Attack Hold
        case ICON_ENV_DR_BACK: // Decay Rate
        case ICON_ENV_DT_BACK: // Decay Target
        case ICON_ENV_SR_BACK: // Sustain Rate
        case ICON_ENV_RR_BACK: // Release Rate
        case ICON_WAV_GT_BACK: // Glide Time
          blk[4]++;
        case ICON_ENV_DY_SLDR:
        case ICON_ENV_AR_SLDR:
        case ICON_ENV_AT_SLDR:
        case ICON_ENV_AH_SLDR:
        case ICON_ENV_DR_SLDR:
        case ICON_ENV_DT_SLDR:
        case ICON_ENV_SR_SLDR:
        case ICON_ENV_RR_SLDR:
        case ICON_WAV_GT_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_WAV_NUM_DEC: midiPlayer_cmds("USER,90,-1"); break; // Wave number
        case ICON_WAV_NUM_INC: midiPlayer_cmds("USER,90,+1"); break; // Wave number
        case ICON_WAV_IPO_DEC: midiPlayer_cmds("USER,93,-1"); break; // Initial Pitch Offset
        case ICON_WAV_IPO_INC: midiPlayer_cmds("USER,93,+1"); break; // Initial Pitch Offset
        case ICON_WAV_FPO_DEC: midiPlayer_cmds("USER,95,-1"); break; // Final Pitch Offset
        case ICON_WAV_FPO_INC: midiPlayer_cmds("USER,95,+1"); break; // Final Pitch Offset
        case ICON_WAV_PM:  midiPlayer_cmds("USER,107,%d", state); break; // Pitch Mod
        case ICON_WAV_AUM: midiPlayer_cmds("USER,109,%d", state); break; // Amp Unsigned Mod
        case ICON_WAV_ASM: midiPlayer_cmds("USER,110,%d", state); break; // Amp Signed Mod
      }
  }

  // Editor wave2 pane
  else if(blk[3] == ro.handle[WIN_ED_WAVE2])
  {
    if(blk[2] == MOUSE_SELECT)
      switch(blk[4])
      {
        case ICON_ENV_DY_BACK: // Delay
        case ICON_ENV_AR_BACK: // Attack Rate
        case ICON_ENV_AT_BACK: // Attack Target
        case ICON_ENV_AH_BACK: // Attack Hold
        case ICON_ENV_DR_BACK: // Decay Rate
        case ICON_ENV_DT_BACK: // Decay Target
        case ICON_ENV_SR_BACK: // Sustain Rate
        case ICON_ENV_RR_BACK: // Release Rate
        case ICON_WAV_GT_BACK: // Glide Time
          blk[4]++;
        case ICON_ENV_DY_SLDR:
        case ICON_ENV_AR_SLDR:
        case ICON_ENV_AT_SLDR:
        case ICON_ENV_AH_SLDR:
        case ICON_ENV_DR_SLDR:
        case ICON_ENV_DT_SLDR:
        case ICON_ENV_SR_SLDR:
        case ICON_ENV_RR_SLDR:
        case ICON_WAV_GT_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_WAV_NUM_DEC: midiPlayer_cmds("USER,91,-1"); break; // Wave number
        case ICON_WAV_NUM_INC: midiPlayer_cmds("USER,91,+1"); break; // Wave number
        case ICON_WAV_IPO_DEC: midiPlayer_cmds("USER,94,-1"); break; // Initial Pitch Offset
        case ICON_WAV_IPO_INC: midiPlayer_cmds("USER,94,+1"); break; // Initial Pitch Offset
        case ICON_WAV_FPO_DEC: midiPlayer_cmds("USER,96,-1"); break; // Final Pitch Offset
        case ICON_WAV_FPO_INC: midiPlayer_cmds("USER,96,+1"); break; // Final Pitch Offset
        case ICON_WAV_PM:  midiPlayer_cmds("USER,108,%d", state); break; // Pitch Mod
        case ICON_WAV_AUM: midiPlayer_cmds("USER,111,%d", state); break; // Amp Unsigned Mod
        case ICON_WAV_ASM: midiPlayer_cmds("USER,112,%d", state); break; // Amp Signed Mod
      }
  }

  // Editor filter pane
  else if(blk[3] == ro.handle[WIN_ED_FILTER])
  {
    if(blk[2] == MOUSE_SELECT)
      switch(blk[4])
      {
        case ICON_ENV_DY_BACK: // Delay
        case ICON_ENV_AR_BACK: // Attack Rate
        case ICON_ENV_AT_BACK: // Attack Target
        case ICON_ENV_AH_BACK: // Attack Hold
        case ICON_ENV_DR_BACK: // Decay Rate
        case ICON_ENV_DT_BACK: // Decay Target
        case ICON_ENV_SR_BACK: // Sustain Rate
        case ICON_ENV_RR_BACK: // Release Rate
        case ICON_FLT_RES_BACK: // Resonance
          blk[4]++;
        case ICON_ENV_DY_SLDR:
        case ICON_ENV_AR_SLDR:
        case ICON_ENV_AT_SLDR:
        case ICON_ENV_AH_SLDR:
        case ICON_ENV_DR_SLDR:
        case ICON_ENV_DT_SLDR:
        case ICON_ENV_SR_SLDR:
        case ICON_ENV_RR_SLDR:
        case ICON_FLT_RES_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_FLT_CF_DEC: midiPlayer_cmds("USER,97,-1"); break; // Cuttoff Frequency
        case ICON_FLT_CF_INC: midiPlayer_cmds("USER,97,+1"); break; // Cuttoff Frequency
        case ICON_FLT_LOW_PASS: midiPlayer_cmds("USER,100,%d", state); break; // Low Pass
        case ICON_FLT_BAND_PASS: midiPlayer_cmds("USER,101,%d", state); break; // Band Pass
        case ICON_FLT_HIGH_PASS: midiPlayer_cmds("USER,102,%d", state); break; // High Pass
        case ICON_FLT_RETRIG: midiPlayer_cmds("USER,117,%d", state); break; // Retrigger
        case ICON_FLT_INVERT: midiPlayer_cmds("USER,123,%d", state); break; // Invert Envelope
        case ICON_FLT_PM: midiPlayer_cmds("USER,114,%d", state); break; // Pitch Mod
        case ICON_FLT_WF: midiPlayer_cmds("USER,103,%d", state); break; // Wave Filter
        case ICON_FLT_PT: midiPlayer_cmds("USER,119,%d", state); break; // Pitch Track
        case ICON_FLT_ET: midiPlayer_cmds("USER,122,%d", state); break; // Envelope Track
          break;
      }
  }

  // Editor noise pane
  else if(blk[3] == ro.handle[WIN_ED_NOISE])
  {
    if(blk[2] == MOUSE_SELECT)
      switch(blk[4])
      {
        case ICON_ENV_DY_BACK: // Delay
        case ICON_ENV_AR_BACK: // Attack Rate
        case ICON_ENV_AT_BACK: // Attack Target
        case ICON_ENV_AH_BACK: // Attack Hold
        case ICON_ENV_DR_BACK: // Decay Rate
        case ICON_ENV_DT_BACK: // Decay Target
        case ICON_ENV_SR_BACK: // Sustain Rate
        case ICON_ENV_RR_BACK: // Release Rate
          blk[4]++;
        case ICON_ENV_DY_SLDR:
        case ICON_ENV_AR_SLDR:
        case ICON_ENV_AT_SLDR:
        case ICON_ENV_AH_SLDR:
        case ICON_ENV_DR_SLDR:
        case ICON_ENV_DT_SLDR:
        case ICON_ENV_SR_SLDR:
        case ICON_ENV_RR_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_NOISE_AM: midiPlayer_cmds("USER,113,%d", state); break; // Amplitude Modulation
        case ICON_NOISE_RETRIG: midiPlayer_cmds("USER,116,%d", state); break; // Retrigger
        case ICON_NOISE_ET: midiPlayer_cmds("USER,121,%d", state); break; // Envelope Track
          break;
      }
  }

  // Editor general pane
  else if(blk[3] == ro.handle[WIN_ED_GENERAL])
  {
    if(blk[2] == MOUSE_SELECT)
      switch(blk[4])
      {
        case ICON_COM_FM_BACK:   // FM Mod Depth
        case ICON_COM_DT_BACK:   // Wave 1/2 Detune
        case ICON_MOD_RATE_BACK: // Modulation rate
        case ICON_MOD_DPTH_BACK: // Modulation Depth
        case ICON_GEN_ERT_BACK:  // Envelope Retrigger Time
        case ICON_GEN_GAIN_BACK: // Instrument Overall Gain
          blk[4]++;
        case ICON_COM_FM_SLDR:
        case ICON_COM_DT_SLDR:
        case ICON_MOD_RATE_SLDR:
        case ICON_MOD_DPTH_SLDR:
        case ICON_GEN_ERT_SLDR:
        case ICON_GEN_GAIN_SLDR:
          ro.slider.window = blk[3];
          ro.slider.icon = blk[4];
          icon_state_change(1, blk[3], blk[4]);
          break;

        case ICON_COM_PT: midiPlayer_cmds("USER,118,%d", state); break; // Common Wave, Pitch Track
        case ICON_COM_ET: midiPlayer_cmds("USER,120,%d", state); break; // Common Wave, Envelope Track
        case ICON_COM_RETRIG: midiPlayer_cmds("USER,115,%d", state); break; // Common Wave, Retrigger
        case ICON_COM_W1_OUT: midiPlayer_cmds("USER,104,%d", state); break; // Wave 1 Out
        case ICON_COM_W2_OUT: midiPlayer_cmds("USER,105,%d", state); break; // Wave 2 Out
        case ICON_COM_WRM: midiPlayer_cmds("USER,106,%d", state); break; // Wave Ring Mod
        case ICON_MOD_NUM_DEC: midiPlayer_cmds("USER,92,-1"); break; // Modulator Wave number
        case ICON_MOD_NUM_INC: midiPlayer_cmds("USER,92,+1"); break; // Modulator Wave number
        case ICON_GEN_DLE: midiPlayer_cmds("USER,124,%d", state); break; // Defined Length Envelopes
        case ICON_GEN_MONO: midiPlayer_cmds("USER,125,%d", state); break; // Mono Instrument
          break;
      }
  }

  else
    ret = 0;

  // check for delete abort
  if((blk[3] != ro.handle[WIN_EDT]) || (blk[4] != ICON_ED_DELETE))
  {
    if(confirmed)
    {
      confirmed = 0;
      if(!(first_valid || swapped))
        icon_text_change("", ro.handle[WIN_EDT], ICON_ED_MESSAGE);
    }
  }

  return ret;
}


/*
 * edt_key_press
 * -------------
 * blk = block from Wimp_GetIconState
 * key = current character
 * p = pointer to icon text
 * Returns True if dealt with here, else False
 */
int edt_key_press(int *blk, int key, char *p)
{
  if((key == 0x19e)||(key == 0x19f)) // PgDown or PgUp, previous or next patch
  {
    midiPlayer_cmds("INSTRUMENT,%c", (key == 0x19e) ? '-' : '+');
    kit_check();
  }

  if(blk[0] == ro.handle[WIN_EDT])
  {
    if(key == '\r')
      switch(blk[1])
      {
        case ICON_ED_PATCH: midiPlayer_cmds("INSTRUMENT,L,%s", p); kit_check(); break; // Patch Number
        case ICON_ED_INST_NAME: midiPlayer_cmds("USER,-1,%s", p); break; // Instrument Name
        case ICON_ED_BANK_NAME: midiPlayer_cmds("USER,-2,%s", p); break; // Bank Name
        case ICON_ED_SOUND_SET: midiPlayer_cmds("USER,-3,%s", p); break; // Sound Set Name
      }
  }

  // editor wave1 pane
  else if(blk[0] == ro.handle[WIN_ED_WAVE1])
  {
    if(key == '\r')
      switch(blk[1])
      {
        case ICON_ENV_DY_VAL: midiPlayer_cmds("USER,4,%s", p); break; // Delay
        case ICON_ENV_AR_VAL: midiPlayer_cmds("USER,5,%s", p); break; // Attack Rate
        case ICON_ENV_AT_VAL: midiPlayer_cmds("USER,6,%s", p); break; // Attack Target
        case ICON_ENV_AH_VAL: midiPlayer_cmds("USER,7,%s", p); break; // Atack Hold
        case ICON_ENV_DR_VAL: midiPlayer_cmds("USER,8,%s", p); break; // Decay Rate
        case ICON_ENV_DT_VAL: midiPlayer_cmds("USER,9,%s", p); break; // Decay Target
        case ICON_ENV_SR_VAL: midiPlayer_cmds("USER,10,%s", p); break; // Sustain Rate
        case ICON_ENV_RR_VAL: midiPlayer_cmds("USER,11,%s", p); break; // Release Rate
        case ICON_WAV_NUM_VAL: midiPlayer_cmds("USER,0,%s", p); break; // Wave Number
        case ICON_WAV_IPO_VAL: midiPlayer_cmds("USER,2,%s", p); break; // Initial Pitch Offset
        case ICON_WAV_FPO_VAL: midiPlayer_cmds("USER,1,%s", p); break; // Final Pitch Offset
        case ICON_WAV_GT_VAL: midiPlayer_cmds("USER,3,%s", p); break; // Glide Time
      }
  }

  // editor wave2 pane
  else if(blk[0] == ro.handle[WIN_ED_WAVE2])
  {
    if(key == '\r')
      switch(blk[1])
      {
        case ICON_ENV_DY_VAL: midiPlayer_cmds("USER,16,%s", p); break; // Delay
        case ICON_ENV_AR_VAL: midiPlayer_cmds("USER,17,%s", p); break; // Attack Rate
        case ICON_ENV_AT_VAL: midiPlayer_cmds("USER,18,%s", p); break; // Attack Target
        case ICON_ENV_AH_VAL: midiPlayer_cmds("USER,19,%s", p); break; // Atack Hold
        case ICON_ENV_DR_VAL: midiPlayer_cmds("USER,20,%s", p); break; // Decay Rate
        case ICON_ENV_DT_VAL: midiPlayer_cmds("USER,21,%s", p); break; // Decay Target
        case ICON_ENV_SR_VAL: midiPlayer_cmds("USER,22,%s", p); break; // Sustain Rate
        case ICON_ENV_RR_VAL: midiPlayer_cmds("USER,23,%s", p); break; // Release Rate
        case ICON_WAV_NUM_VAL: midiPlayer_cmds("USER,12,%s", p); break; // Wave Number
        case ICON_WAV_IPO_VAL: midiPlayer_cmds("USER,14,%s", p); break; // Initial Pitch Offset
        case ICON_WAV_FPO_VAL: midiPlayer_cmds("USER,13,%s", p); break; // Final Pitch Offset
        case ICON_WAV_GT_VAL: midiPlayer_cmds("USER,15,%s", p); break; // Glide Time
      }
  }

  // editor filter pane
  else if(blk[0] == ro.handle[WIN_ED_FILTER])
  {
    if(key == '\r')
      switch(blk[1])
      {
        case ICON_ENV_DY_VAL: midiPlayer_cmds("USER,32,%s", p); break; // Delay
        case ICON_ENV_AR_VAL: midiPlayer_cmds("USER,33,%s", p); break; // Attack Rate
        case ICON_ENV_AT_VAL: midiPlayer_cmds("USER,34,%s", p); break; // Attack Target
        case ICON_ENV_AH_VAL: midiPlayer_cmds("USER,35,%s", p); break; // Atack Hold
        case ICON_ENV_DR_VAL: midiPlayer_cmds("USER,36,%s", p); break; // Decay Rate
        case ICON_ENV_DT_VAL: midiPlayer_cmds("USER,37,%s", p); break; // Decay Target
        case ICON_ENV_SR_VAL: midiPlayer_cmds("USER,38,%s", p); break; // Sustain Rate
        case ICON_ENV_RR_VAL: midiPlayer_cmds("USER,39,%s", p); break; // Release Rate
        case ICON_FLT_CF_VAL: midiPlayer_cmds("USER,41,%s", p); break; // Cutoff Frequency
        case ICON_FLT_RES_VAL: midiPlayer_cmds("USER,42,%s", p); break; // Resonance
      }
  }

  // editor noise pane
  else if(blk[0] == ro.handle[WIN_ED_NOISE])
  {
    if(key == '\r')
      switch(blk[1])
      {
        case ICON_ENV_DY_VAL: midiPlayer_cmds("USER,24,%s", p); break; // Delay
        case ICON_ENV_AR_VAL: midiPlayer_cmds("USER,25,%s", p); break; // Attack Rate
        case ICON_ENV_AT_VAL: midiPlayer_cmds("USER,26,%s", p); break; // Attack Target
        case ICON_ENV_AH_VAL: midiPlayer_cmds("USER,27,%s", p); break; // Atack Hold
        case ICON_ENV_DR_VAL: midiPlayer_cmds("USER,28,%s", p); break; // Decay Rate
        case ICON_ENV_DT_VAL: midiPlayer_cmds("USER,29,%s", p); break; // Decay Target
        case ICON_ENV_SR_VAL: midiPlayer_cmds("USER,30,%s", p); break; // Sustain Rate
        case ICON_ENV_RR_VAL: midiPlayer_cmds("USER,31,%s", p); break; // Release Rate
      }
  }

  // editor noise pane
  else if(blk[0] == ro.handle[WIN_ED_GENERAL])
  {
    if(key == '\r')
      switch(blk[1])
      {
        case ICON_COM_FM_VAL: midiPlayer_cmds("USER,47,%s", p); break; // FM Mod Depth
        case ICON_COM_DT_VAL: midiPlayer_cmds("USER,48,%s", p); break; // Wave 1/2 Detune
        case ICON_MOD_NUM_VAL: midiPlayer_cmds("USER,44,%s", p); break; // Modulator Wave number
        case ICON_MOD_RATE_VAL: midiPlayer_cmds("USER,45,%s", p); break; // Modulation rate
        case ICON_MOD_DPTH_VAL: midiPlayer_cmds("USER,46,%s", p); break; // Modulation Depth
        case ICON_GEN_ERT_VAL: midiPlayer_cmds("USER,43,%s", p); break; // Envelope Retrigger Time
        case ICON_GEN_GAIN_VAL: midiPlayer_cmds("USER,49,%s", p); break; // Instrument Overall Gain
      }
  }

  else if(blk[0] == ro.handle[WIN_KBD])
  {
    key_scan(1);
  }

  else
    return 0;

  return 1;
}


/*
 * display_patch
 * -------------
 * n = channel number
 */
void display_patch(int n)
{
  chan_t *c = &syn.chan[n];

  myprintf("Channel %d, Program %d, Bank %d:%d",
         (n & (NUM_MIDI_CHANS-1)) + 1, c->patch.prg, c->patch.hi, c->patch.lo);

  if((n & (NUM_MIDI_CHANS-1)) == 9)
    myprintf(", percussion");
  else
  {
    int b = syn.idat.banks[c->patch.lo];
    if(b)
    {
      int i = syn.idat.bank[b-1].ins[c->patch.prg];
      if(i)
        myprintf(", %s", syn.idat.instrument[i-1].name);
    }
  }
  myprintf("\n");
}


/*
 * load_instrument
 * ---------------
 * loads the syn.user (ram) instrument from a preset (flash) instrument.
 * num = instrument patch number
 */
int load_instrument(int num)
{
  ins_data_t* idat = &syn.idat;

  if(num < 0) num = 0;
  else if (num >= syn.kbd_patch.num) num = syn.kbd_patch.num - 1;

  int prg = syn.kbd_patch.list[num].prg;
  int hi  = syn.kbd_patch.list[num].hi;
  int lo  = syn.kbd_patch.list[num].lo;

  if(hi == PERCUSSION_BANK)
    syn.m_out.channel = KBD_CHAN_OFFSET + PERCUSSION_CHAN - 1; // Key mapped GM Drum Kit (not for editing)
  else
  {
    ins_t *i = NULL;

    if(hi == PERCUSSION_INST)
    {
      if(idat->kits[prg])
        if(idat->bank[idat->kits[prg]-1].ins)
          i = &idat->instrument[idat->bank[idat->kits[prg]-1].ins[lo]-1]; // GM Drum Kit part for editing
    }
    else if(idat->banks[lo])
      if(idat->bank[idat->banks[lo]-1].ins)
        i = &idat->instrument[idat->bank[idat->banks[lo]-1].ins[prg]-1];

    // no fall back to GM when loading voices that are undefined
    if(i == NULL)
      return -UNDEFINED;

    syn.user = *i; // load the syn.user (ram) instrument
    syn.m_out.channel = KBD_CHAN_OFFSET;

//    if(hi == PERCUSSION_INST)
//    {
//      // pitched Drum Kit part
//      syn.user.switches |= (1<<PITCH_TRACK);
//      syn.user.wave[0].pitch -= C5;
//      syn.user.wave[1].pitch -= C5;
//    }
  }

  syn.chan[syn.m_out.channel].patch = syn.kbd_patch.list[num];

  reset_mono(); // reset any generators that might be ignored by having been mono assigned

  // set keyboard mode depending on instrument type
  syn.switches = (syn.switches & ~(1<<POLY_MODE)) | (((syn.user.switches >> MONO_SCAN) & 1) << POLY_MODE);

//  myprintf("Instrument %d\n", num);
  syn.kbd_patch.cur = num;
  display_user(); // update gui

  unsigned char buff[3] = {PROGRAM + syn.m_out.channel - KBD_CHAN_OFFSET, prg, 0};

  if(mpg.debug & (1<<DEBUG_MIDI))
    print_midi_msg(EDITOR, buff[0], buff+1, 2);

  return NO_ERROR_;
}


/*
 * cmd_instrument
 * --------------
 * Performs various operations on syn.user and stored instruments.
 */
int cmd_instrument(char *msg, int len)
{
  if(len == 0) // display status
    myprintf("Instrument = %d\n", syn.kbd_patch.cur);

  else if(msg[0] == '?') // display help
    myprintf(" Loads the keyboard instrument.\n"
           "  ,L,<instr>  Load the instrument, 0 to %d\n"
           "  ,+          Load the next instrument\n"
           "  ,-          Load the previous instrument\n"
           , syn.kbd_patch.num-1);

  else if(msg[0] == '!') // report for controller
    myprintf("INSTRUMENT,%d\n", syn.kbd_patch.cur);

  else
    switch(msg[1])
    {
      case '+': // next
        syn.kbd_patch.cur++;
        if(syn.kbd_patch.cur >= syn.kbd_patch.num) syn.kbd_patch.cur = 0;
        return load_instrument(syn.kbd_patch.cur);

      case '-': // perevious
        syn.kbd_patch.cur--;
        if(syn.kbd_patch.cur < 0) syn.kbd_patch.cur = syn.kbd_patch.num - 1;
        return load_instrument(syn.kbd_patch.cur);

      case 'L': // load
      case 'l':
        return load_instrument(atoi(msg+3));

      default:
        return -PARAMETER_ERROR;
    }

  return NO_ERROR_;
}

/*
 * cmd_bank
 * --------
 * Sets the midi out keyboard bank high and bank low and displays the channel patch.
 */
int cmd_bank(char *msg, int len)
{
  int hi, lo;

  if(len == 0) // display status
    display_patch(syn.m_out.channel);

  else if(msg[0] == '?') // display help
    myprintf(" Sets the keyboard bank high and bank low and displays the channel patch\n"
             "  ,<hi>,<lo>   Set the <hi> and <lo> bank numbers\n"
             "          bank numbers are from 0 to %d\n", MIDI_DATA_RANGE-1);

  else if(sscanf(msg, ",%d,%d", &hi, &lo) != 2)
    return -PARAMETER_ERROR;

  else if((hi < 0) || (hi >= MIDI_DATA_RANGE) || (lo < 0) || (lo >= MIDI_DATA_RANGE))
    return -OUT_OF_RANGE;

  else
  {
    syn.chan[syn.m_out.channel].patch.hi = hi;
    syn.chan[syn.m_out.channel].patch.lo = lo;
    display_patch(syn.m_out.channel);
  }

  return NO_ERROR_;
}


/*
 * cmd_program
 * -----------
 * Sets the midi out keyboard channel program and displays the channel patch.
 */
int cmd_program(char *msg, int len)
{
  int prg;

  if(len == 0) // display status
    display_patch(syn.m_out.channel);

  else if(msg[0] == '?') // display help
    myprintf(" Sets the keyboard channel program and displays the channel patch.\n"
             "  ,<prg>   Set the channel program, 0 to %d\n", MIDI_DATA_RANGE-1);

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

  else if((prg < 0) || (prg >= MIDI_DATA_RANGE))
    return -OUT_OF_RANGE;

  else
  {
    syn.chan[syn.m_out.channel].patch.prg = prg;
    display_patch(syn.m_out.channel);
  }

  return NO_ERROR_;
}


/*
 * cmd_channel
 * -----------
 * Sets the midi out keyboard channel and displays the channel patch.
 */
int cmd_channel(char *msg, int len)
{
  int ch;

  if(len == 0) // display status
    myprintf("MIDI out channel = %d\n", syn.m_out.channel - KBD_CHAN_OFFSET + 1);

  else if(msg[0] == '?') // display help
    myprintf(" Sets the keyboard channel and displays the channel patch.\n"
             "  ,<chan>  Set the channel number, 1 to %d\n", NUM_MIDI_CHANS);

  else if(msg[0] == '!') // report for controller
  {
    chan_t *c = &syn.chan[syn.m_out.channel];
    myprintf("CHANNEL,%d,%d,%d,%d\n", syn.m_out.channel - KBD_CHAN_OFFSET + 1, c->patch.prg, c->patch.hi, c->patch.lo);
  }

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

  else if((ch < 1) || (ch > NUM_MIDI_CHANS))
    return -OUT_OF_RANGE;

  else
  {
    syn.m_out.channel = ch + KBD_CHAN_OFFSET - 1;
    display_patch(syn.m_out.channel);
  }

  return NO_ERROR_;
}


/*
 * cmd_velocity
 * ------------
 * Sets the midi out keyboard velocity and displays the channel patch.
 */
int cmd_velocity(char *msg, int len)
{
  int vel;

  if(len == 0) // display status
    myprintf("MIDI out key velocity = %d\n", syn.m_out.velocity);

  else if(msg[0] == '?') // display help
    myprintf(" Sets the keyboard velocity and displays the channel patch.\n"
             "  ,<vel>  Set the key velocity, 0 to %d\n", MIDI_DATA_RANGE-1);

  else if(msg[0] == '!') // report for controller
    myprintf("VELOCITY,%d\n", syn.m_out.velocity);

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

  else if((vel < 0) || (vel >= MIDI_DATA_RANGE))
    return -OUT_OF_RANGE;

  else
  {
    syn.m_out.velocity = vel;
    display_patch(syn.m_out.channel);
  }

  return NO_ERROR_;
}




