/***************************************************************************
*
* FunktrackerGOLD - By Jason Nunn
*
* Using Curses (ANSI C) NON-preemptive Processing
*
* Snail: 32 Rothdale Road, Moil, Darwin, NT, 0810, Australia
*
* =======================================================
*
* Pattern Editor
*
***************************************************************************/
#include <ncurses.h>
#include <string.h>
#pragma pack(1)
#include "everything.h"
#include "funktracker.h"
#pragma pack()
#include "funkgold.h"

/*protos dsp_mixxer.c*/
extern int no_active_channels;

/*protos funktracker.c*/
#pragma pack(1)
extern tfunk_info funk_info;
extern tfunk_hr *funk_hr_ptr;
extern tslot *funk_pat_ptr;
extern tfunk_chan funk_chan[MAXIMUM_CHANNELS];
extern void *funk_sam_ptrs[64];
extern int c_channel;
extern int do_retrig_sample(int sam_no);
extern void do_full_note(int note,int volume);
#pragma pack()

/*protos funkmisc.c*/
extern void clear_slot(tslot *slot);
extern void funk_init_for_play(void);
extern void (*virtualmixxer)(void);

/*protos funkgold_main.c*/
extern int ch,maxx,maxy;
extern char edit_mode;
extern char nibble_display[];
extern char *note_display[];
extern void display_topbar(void);
extern void update_screen(void);
extern void clear_area(char y1,char x1,char y2,char x2);
extern void byte2str(char *str,unsigned char value);
extern int hex2nib(char *str);
extern int hex2char(char *str);
extern void get_string(char y, char x,char *str,int maxlen);
extern void main_editor_dis_bg(void);
extern void save_song(int song_type);
extern int quit_confirm(void);

/*protos funkgold_sm.c*/
extern unsigned char sm_pos_real;
extern unsigned char sm_pos_hl;

/*protos funkgold_se.c*/
extern unsigned char se_pos_real;
extern unsigned char se_pos_hl;

/*protos funkgold_trac.c*/
extern void play_song(int play_type);

/*locals*/
#define SLOT_HL     0
#define SLOT_UNHL   1

#pragma pack(1)
tslot *pe_slot;
tslot *pe_copy_buffer;
#pragma pack()

unsigned char pe_pattern_no;
unsigned char pe_note_no;
unsigned char pe_octave_no;
unsigned char pe_chan_real;
unsigned char pe_pos_real;
unsigned char pe_chan_hl;
unsigned char pe_pos_hl;
unsigned char pe_chan_hl_old;
unsigned char pe_pos_hl_old;
unsigned char pe_nodischans;

/*copying*/
unsigned char pe_mchan_begin;
unsigned char pe_mchan_end;
unsigned char pe_mpos_begin;
unsigned char pe_mpos_end;
unsigned char pe_mflag;
unsigned char pe_copied_flag;

/***************************************************************************
*
***************************************************************************/
void play_slot(int note_value)
{
  if(funk_sam_ptrs[sm_pos_real + sm_pos_hl] != NULL)
  {
    funk_init_for_play();
    funk_info.trek_status = STOP;
    c_channel = pe_chan_real + pe_chan_hl;
    do_full_note(note_value,do_retrig_sample(sm_pos_real + sm_pos_hl));
    nodelay(stdscr,TRUE);
    ch = ERR;
    while(ch == ERR)
    {
      virtualmixxer();
      ch = wgetch(stdscr);
    }
    nodelay(stdscr,FALSE);
    ungetch(ch);
  }
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void display_oct_no(void)
{
  char tmpstr[] = "  ";

  move(maxy - 4,37);
  byte2str(tmpstr,pe_octave_no + 1);
  addstr(tmpstr);
}

void dec_oct_no(void)
{
  if(pe_octave_no > 0) pe_octave_no--;
  display_oct_no();
  update_screen();
}

void inc_oct_no(void)
{
  if(pe_octave_no < 4) pe_octave_no++;
  display_oct_no();
  update_screen();
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void pe_dis_seqpat_no_bg(void)
{
  register int amaxy = maxy - 4;

  attroff(A_REVERSE);
  move(amaxy,0);
  addstr("Order No:");
  move(amaxy,14);
  addstr("Pattern No:");
}

void pe_display_bg(void)
{
  register int x,y;
  register int amaxy = maxy - 5;

  display_topbar();
  move(1,0);
  attroff(A_REVERSE);
  addstr("Pattern Editor");
  attron(A_REVERSE);
  move(2,0);
  addstr("    ");
  move(amaxy,0);
  addstr("    ");
  for(x = 0;x < pe_nodischans;x++)
  {
    move(amaxy,(x * 9) + 4);
    addstr("          ");
  }
  for(y = 0;y < (maxy - 7);y++)
  {
    move(y + 3,0);
    addch(' ');
    for(x = 0;x <= pe_nodischans;x++)
    {
      move(y + 3,(x * 9) + 4);
      addch(' ');
    }
  }
  pe_dis_seqpat_no_bg();
  amaxy = maxy - 4;
  move(amaxy,30);
  addstr("Octave:");
  move(amaxy,42);
  addstr("Sample:");
}

/***************************************************************************
*
***************************************************************************/
void pe_display_slot(
  unsigned char chan_real,
  unsigned char pos_real,
  unsigned char chan_hl,
  unsigned char pos_hl)
{
  char dis_slot[] = "     ---";
  register unsigned char not_no,sam_no,com_no;
#pragma pack(1)
  tslot *slot;
#pragma pack()

  move(3 + pos_hl,5 + (chan_hl * 9));
  if((pos_real + pos_hl) < 64)
  {
    slot = 
      pe_slot +
      ((pos_real + pos_hl) * no_active_channels) +
      (chan_real + chan_hl);
    not_no = slot->not_sam >> 2;
    sam_no = ((slot->not_sam & 0x3) << 4) + (slot->sam_com >> 4);
    com_no = (slot->sam_com & 0x0f);
    if(not_no != 0x3f)
    {
      byte2str(dis_slot,sam_no);
      strncpy(&dis_slot[2],note_display[not_no],3);
    }
    if(com_no != 0x0f)
    {
      dis_slot[5] = 'a' + com_no;
      byte2str(dis_slot + 6,slot->com_val);
    }
    addstr(dis_slot);
  }
  else
    addstr("        ");
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void pe_hl_mslot(int pos,int chan)
{
  if((chan >= pe_mchan_begin) && (chan <= pe_mchan_end) &&
     (pos >= pe_mpos_begin) && (pos <= pe_mpos_end))
    attron(A_REVERSE);
  else
    attroff(A_REVERSE);
}

void pe_display_trak(
  unsigned char pat_no,
  unsigned char chan_real,
  unsigned char pos_real)
{
  register unsigned char chan_hl,pos_hl;
  register int amaxy = maxy - 8;

  pe_slot = funk_pat_ptr + (pat_no * 64 * no_active_channels);
  if(pe_mflag)
    for(pos_hl = 0;pos_hl < amaxy;pos_hl++)
      for(chan_hl = 0;chan_hl < pe_nodischans;chan_hl++)
      {
        pe_hl_mslot(pos_hl + pe_pos_real,chan_hl + pe_chan_real);
        pe_display_slot(chan_real,pos_real,chan_hl,pos_hl);
      }
  else
    for(pos_hl = 0;pos_hl < amaxy;pos_hl++)
      for(chan_hl = 0;chan_hl < pe_nodischans;chan_hl++)
        pe_display_slot(chan_real,pos_real,chan_hl,pos_hl);
}

void pe_display_ind(unsigned char pat_no,unsigned char pos_real)
{
  register unsigned char pos_hl,x;
  char str[] = "   ";

  for(pos_hl = 0;pos_hl < (maxy - 8);pos_hl++)
  {
    x = pos_real + pos_hl;
    if(x < 64)
    {
      byte2str(str,x);
      if(x == funk_hr_ptr->break_list[pat_no])
        str[2] = '*';
      else
        str[2] = ' ';
    }
    else
      strcpy(str,"   ");
    move(3 + pos_hl,1);
    addstr(str);
  }
}

void pe_dischan_bg(void)
{
  char str[10];
  register int x;

  attron(A_REVERSE);
  for(x = 0;x < pe_nodischans;x++)
  {
    register chan_no = pe_chan_real + x;
    move(2,(x * 9) + 4);
    if(funk_chan[chan_no].channel_kill == PLAY)
      sprintf(str,"  Chan %c  ",nibble_display[chan_no]);
    else
      sprintf(str,"   OFF %c  ",nibble_display[chan_no]);
    addstr(str);
  }
}

/* miwi 27/05/96*/
void pe_display_sam_no(void)
{
  char tmpstr[] = "00 <0123456789012345678>";
  int sam_no = sm_pos_real + sm_pos_hl;

  move(maxy - 4,49);
  byte2str(tmpstr,sam_no);
  memcpy(&(tmpstr[4]),funk_hr_ptr->funk_sb[sam_no].sname,19);
  addstr(tmpstr);
}

/* miwi 27/05/96*/
void pe_display_all(void)
{
  char tmpstr[] = "  ";
  register int amaxy = maxy - 4;

  pe_display_trak(pe_pattern_no,pe_chan_real,pe_pos_real);
  attroff(A_REVERSE);
  pe_display_ind(pe_pattern_no,pe_pos_real);
  pe_dischan_bg();
  attron(A_REVERSE);
  pe_display_slot(pe_chan_real,pe_pos_real,pe_chan_hl,pe_pos_hl);
  attroff(A_REVERSE);

  move(amaxy,9);
  byte2str(tmpstr,se_pos_real + se_pos_hl);
  addstr(tmpstr);
  move(amaxy,25);
  byte2str(tmpstr,pe_pattern_no);
  addstr(tmpstr);
  display_oct_no();
  pe_display_sam_no();

  pe_chan_hl_old = pe_chan_hl;
  pe_pos_hl_old = pe_pos_hl;
}

void pe_move_slot(void)
{
  register unsigned char chan,pos;

  pe_slot = funk_pat_ptr + (pe_pattern_no * 64 * no_active_channels);
  if(pe_mflag)
    pe_hl_mslot(pe_pos_real + pe_pos_hl_old,pe_chan_real + pe_chan_hl_old);
  else
    attroff(A_REVERSE);
  pe_display_slot(pe_chan_real,pe_pos_real,pe_chan_hl_old,pe_pos_hl_old);

  if(pe_mflag)
  {
    pos = pe_pos_real + pe_pos_hl;
    chan = pe_chan_real + pe_chan_hl;
    if((chan >= pe_mchan_begin) && (chan <= pe_mchan_end) &&
       (pos >= pe_mpos_begin) && (pos <= pe_mpos_end))
      attroff(A_REVERSE);
    else
      attron(A_REVERSE);
  }
  else
    attron(A_REVERSE);
  pe_display_slot(pe_chan_real,pe_pos_real,pe_chan_hl,pe_pos_hl);
  attroff(A_REVERSE);
  pe_chan_hl_old = pe_chan_hl;
  pe_pos_hl_old = pe_pos_hl;
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void pe_cursor_up(void)
{
  if(pe_pos_hl > 0)
  {
    pe_pos_hl--;
    pe_move_slot();
    update_screen();
  }
  else
  {
    if(pe_pos_real > 0) pe_pos_real--;
    pe_display_all();
    update_screen();
  }
}

void pe_cursor_down(void)
{
  if(pe_pos_hl < (maxy - 9))
  {
    pe_pos_hl++;
    pe_move_slot();
    update_screen();
  }
  else
  {
    if(pe_pos_real < ((64 + 8) - maxy)) pe_pos_real++;
    pe_display_all();
    update_screen();
  }
}

void pe_cursor_left(void)
{
  if(pe_chan_hl > 0)
  {
    pe_chan_hl--;
    pe_move_slot();
    update_screen();
  }
  else
  {
    if(pe_chan_real > 0) pe_chan_real--;
    pe_display_all();
    update_screen();
  }
}

void pe_cursor_right(void)
{
  if(pe_chan_hl < (pe_nodischans - 1))
  {
    pe_chan_hl++;
    pe_move_slot();
    update_screen();
  }
  else
  {
    if(pe_nodischans < no_active_channels)
    {
      if(pe_chan_real < (no_active_channels - pe_nodischans))
        pe_chan_real++;
      pe_display_all();
      update_screen();
    }
  }
}
 
/***************************************************************************
*
* Slot edit functions
*
***************************************************************************/
#define calc_slot pe_slot = \
  funk_pat_ptr + \
  (pe_pattern_no * 64 * no_active_channels) + \
  ((pe_pos_real + pe_pos_hl) * no_active_channels) + \
  (pe_chan_real + pe_chan_hl);

void pe_enter_slot(void)
{
  register int sam_no = sm_pos_real + sm_pos_hl;

  calc_slot
  pe_slot->not_sam = ((pe_octave_no * 12) + pe_note_no) << 2;
  pe_slot->not_sam |= (sam_no >> 4) & 0x3;
  pe_slot->sam_com = (sam_no << 4) | 0xf;
  pe_slot->com_val = 0;
  pe_cursor_down();
}

void pe_clr_slot(void)
{
  calc_slot
  clear_slot(pe_slot);
  pe_cursor_down();
}

void pe_edit_slot_sam(void)
{
  char str[3];
  register int sam_no;

  attroff(A_REVERSE);
  pe_display_slot(pe_chan_real,pe_pos_real,pe_chan_hl,pe_pos_hl);
  refresh();
  attron(A_REVERSE);
  get_string(3 + pe_pos_hl,5 + (pe_chan_hl * 9),str,2);
  sam_no = hex2char(str);
  if(sam_no != -1)
  {
    if(sam_no > 64) sam_no = 63;
    calc_slot
    if((pe_slot->not_sam >> 2) == 0x3f)
      pe_slot->not_sam = 0x3e << 2;
    else
      pe_slot->not_sam &= 0xfc;
    pe_slot->not_sam |= (sam_no >> 4) & 0x3;
    pe_slot->sam_com = (sam_no << 4) | 0xf;
  }
  pe_slot = funk_pat_ptr + (pe_pattern_no * 64 * no_active_channels);
  pe_display_slot(pe_chan_real,pe_pos_real,pe_chan_hl,pe_pos_hl);
}

void pe_edit_slot_com(void)
{
  char str[4];
  register int com_val;

  attroff(A_REVERSE);
  pe_display_slot(pe_chan_real,pe_pos_real,pe_chan_hl,pe_pos_hl);
  refresh();
  attron(A_REVERSE);
  get_string(3 + pe_pos_hl,10 + (pe_chan_hl * 9),str,3);
  if(strlen(str) == 0)
  {
    calc_slot
    pe_slot->sam_com |= 0x0f;
    pe_slot->com_val = 0;
  }
  else
  {
    if((str[0] >= 'a') && (str[0] <= 'o')) str[0] -= ('a' - 'A');
    if((str[0] >= 'A') && (str[0] <= 'O'))
    {
      if((com_val = hex2char(str + 1)) != -1)
      {
        calc_slot
        pe_slot->sam_com &= 0xf0;
        pe_slot->sam_com |= str[0] - 'A';
        pe_slot->com_val = com_val;
      }
    }
  }
  pe_slot = funk_pat_ptr + (pe_pattern_no * 64 * no_active_channels);
  pe_display_slot(pe_chan_real,pe_pos_real,pe_chan_hl,pe_pos_hl);
}

void pe_enter_RLO(void)
{
  calc_slot
  pe_slot->not_sam &= 0x3;
  pe_slot->not_sam |= 0x3d << 2;
  pe_cursor_down();
}

/***************************************************************************
*
***************************************************************************/
void pe_insert_slot(void)
{
  register int x;
#pragma pack(1)
  tslot *tmp_slot;
#pragma pack()

  tmp_slot = funk_pat_ptr +
    (pe_pattern_no * 64 * no_active_channels) + 
    (0x3f * no_active_channels) +
    (pe_chan_real + pe_chan_hl);
  for(x = 0x3f;x > (pe_pos_real + pe_pos_hl);x--)
  {
    memcpy(tmp_slot,tmp_slot - no_active_channels,sizeof(tslot));
    tmp_slot -= no_active_channels;
  }
  calc_slot
  clear_slot(pe_slot);
  pe_display_all();
  update_screen();
}

void pe_delete_slot(void)
{
  register int x;
#pragma pack(1)
  tslot *tmp_slot;
#pragma pack()

  calc_slot
  for(x = (pe_pos_real + pe_pos_hl);x < 0xff;x++)
  {
    memcpy(pe_slot,pe_slot + no_active_channels,sizeof(tslot));
    pe_slot += no_active_channels;
  }
  tmp_slot = funk_pat_ptr +
    (pe_pattern_no * 64 * no_active_channels) + 
    (0x3f * no_active_channels) +
    (pe_chan_real + pe_chan_hl);
  clear_slot(tmp_slot);
  pe_display_all();
  update_screen();
}

/***************************************************************************
*
***************************************************************************/
int mus_kb_input(void)
{
  register int nv = -1;

  switch(ch)
  {
    case FC_NOTE_C:  nv =  0; break;
    case FC_NOTE_CS: nv =  1; break;
    case FC_NOTE_D:  nv =  2; break;
    case FC_NOTE_DS: nv =  3; break;
    case FC_NOTE_E:  nv =  4; break;
    case FC_NOTE_F:  nv =  5; break;
    case FC_NOTE_FS: nv =  6; break;
    case FC_NOTE_G:  nv =  7; break;
    case FC_NOTE_GS: nv =  8; break;
    case FC_NOTE_A:  nv =  9; break;
    case FC_NOTE_AS: nv = 10; break;
    case FC_NOTE_B:  nv = 11; break;
  }
  return nv;
}

/***************************************************************************
* PE copy routines
*
* miwi 27/05/96
***************************************************************************/
void pe_begin_mark(void)
{
  pe_mpos_end = pe_mpos_begin = pe_pos_real + pe_pos_hl;
  pe_mchan_end = pe_mchan_begin = pe_chan_real + pe_chan_hl;
  pe_copied_flag = 0;
  pe_mflag = 1;
  pe_display_all();
  update_screen();
}

void pe_end_mark(void)
{
  pe_mpos_end = pe_pos_real + pe_pos_hl;
  pe_mchan_end = pe_chan_real + pe_chan_hl;
  pe_copied_flag = 0;
  if(pe_mpos_end < pe_mpos_begin) pe_mpos_end = pe_mpos_begin;
  if(pe_mchan_end < pe_mchan_begin) pe_mchan_end = pe_mchan_begin;
  pe_display_all();
  update_screen();
}

void pe_copy_mark(void)
{
#pragma pack(1)
  tslot *tmp_slot;
#pragma pack()

  if(pe_mflag)
  {
    tmp_slot = funk_pat_ptr +
      (pe_pattern_no * 64 * no_active_channels);
    memcpy(pe_copy_buffer,tmp_slot,no_active_channels * 64 * sizeof(tslot));
    pe_copied_flag = 1;
    pe_mflag = 0;
    pe_display_all();
    move((maxy - 1),0);
    addstr("Block copied.");
    update_screen();
  }
}

void pe_delete_mark(void)
{
#pragma pack(1)
  tslot *tmp_slot;
#pragma pack()
  register unsigned char pos,chan;

  if(pe_mflag)
  {
    tmp_slot = funk_pat_ptr +
      (pe_pattern_no * 64 * no_active_channels);
    for(pos = pe_mpos_begin;pos <= pe_mpos_end;pos++)
      for(chan = pe_mchan_begin;chan <= pe_mchan_end;chan++)
        clear_slot(tmp_slot + (pos * no_active_channels) + chan);
    pe_mflag = 0;
    pe_display_all();
    move((maxy - 1),0);
    addstr("Block deleted.");
    update_screen();
  }
}

void pe_paste_mark(void)
{
#pragma pack(1)
  tslot *tmp_slot;
#pragma pack()
  register unsigned char pos,chan,posdst,chandst;

  if(pe_copied_flag)
  {
    tmp_slot = funk_pat_ptr +
      (pe_pattern_no * 64 * no_active_channels);
    posdst = pe_pos_real + pe_pos_hl;
    for(pos = pe_mpos_begin;pos <= pe_mpos_end;pos++)
    {
      chandst = pe_chan_real + pe_chan_hl;
      for(chan = pe_mchan_begin;chan <= pe_mchan_end;chan++)
      {
        if((chandst < no_active_channels) && (posdst < 64))
        {
          (tmp_slot + (posdst * no_active_channels) + chandst)->not_sam =
            (pe_copy_buffer + (pos * no_active_channels) + chan)->not_sam;
          (tmp_slot + (posdst * no_active_channels) + chandst)->sam_com =
            (pe_copy_buffer + (pos * no_active_channels) + chan)->sam_com;
          (tmp_slot + (posdst * no_active_channels) + chandst)->com_val =
            (pe_copy_buffer + (pos * no_active_channels) + chan)->com_val;
        }
        chandst++;
      }
      posdst++;
    }
    pe_display_all();
    move((maxy - 1),0);
    addstr("Block pasted.");
    update_screen();
  }
}

/***************************************************************************
*
***************************************************************************/
void pe_dec_sam(void)
{
  if(sm_pos_hl > 0)
  {
    sm_pos_hl--;
    pe_display_sam_no();
    update_screen();
  }
  else
    if(sm_pos_real > 0)
    {
      sm_pos_real--;
      pe_display_sam_no();
      update_screen();
    }
}

void pe_inc_sam(void)
{
  if(sm_pos_hl < (maxy - 9))
  {
    sm_pos_hl++;
    pe_display_sam_no();
    update_screen();
  }
  else
    if(sm_pos_real < ((64 + 8) - maxy))
    {
      sm_pos_real++;
      pe_display_sam_no();
      update_screen();
    }
}

/***************************************************************************
* Command Assistant
***************************************************************************/
char *pe_cmd_list[] =
{
  "Axx Frequency Port Up",
  "Bxx Frequency Port Down",
  "Cxx Frequency Porta",
  "Dxx Frequency Vibrato",
  "Exx Freq Vibrato Fanin",
  "Fxx Freq Vibrato Fanout",
  "Gxx Volume Slide Up",
  "Hxx Volume Slide Down",
  "Ixx Volume Porta",
  "Jxx Volume Reverb",
  "Kxx Tremolo",
  "Lxx Arpeggio",
  "Mxx Sample Offset",
  "Nxx Volume",
  "O00 Set Vibrato waveform to Sine",
  "O01 Set Vibrato waveform to Triangle",
  "O02 Set Vibrato waveform to Square",
  "O03 Set Vibrato waveform to Sawtooth",
  "O04 Set Vibrato waveform to Random",
  "O05 Set Tremolo waveform to Sine",
  "O06 Set Tremolo waveform to Triangle",
  "O07 Set Tremolo waveform to Square",
  "O08 Set Tremolo waveform to Sawtooth",
  "O09 Set Tremolo waveform to Random",
  "O0A Halt Note System",
  "O0B Halt Volume System",
  "O0C Halt All Systems",
  "O0D NOT USED",
  "O0E Fine channel pann Left",
  "O0F Fine channel pann right",
  "O1x Volume Cut",
  "O2x Note delay",
  "O3x Set Arpeggio Speed",
  "O4x Fine Port Up",
  "O5x Fine Port Down",
  "O6x Fine Volume Slide Up",
  "O7x Fine Volume Slide Down",
  "O8x Volume Crest",
  "O9x Set channel Echo Feedback Gain",
  "OAx Set Master Volume",
  "OBx Set channel Echo Delay",
  "OCx Set channel Echo Decay",
  "ODx Note Retrig",
  "OEx Set Channel Balance (x16)",
  "OFx Tempo"
};

char *pe_cmd_list_des[] =
{
  "  A 00000000  (Note System)",
  "    | rate |",
  "",
  "Will slide (increase) the frequency",
  "of a channel. The larger the rate the",
  "faster the slide.",
  "",
  "",

  "  B 00000000  (Note System)",
  "    | rate |",
  "",
  "Same as Cmd A, accept frequency",
  "is decreased.",
  "",
  "",
  "",

  "  C 00000000  (Note System)",
  "    | rate |",
  "",
  "Can only be used in 'Full Slots'. It",
  "ports the current note of a given",
  "channel to the frequency of the given",
  "full slot. Bigger rate, faster slide.",
  "",

  "  D 00000000  (Note System)",
  "    |Sp||Ap|",
  "",
  "Vibrates the freq of channel. 'Sp'",
  "sets the speed. The smaller 'Sp' the",
  "faster the vibrato. 'Ap' is the amp.",
  "The bigger the 'Ap'- bigger the vib.",
  "Subject to waveforms. See 'O0' cmds.",

  "  E 00000000  (Note System)",
  "    |Sp||Ap|",
  "",
  "Is extension of Vib cmd. Should be",
  "used after Vib.. Slow's down the vib.",
  "over a period of time. 'Sp' is spd of",
  "the slide (smaller- faster slide).",
  "'Ap' is amplitude of the vibrato.",

  "  F 00000000  (Note System)",
  "    |Sp||Ap|",
  "",
  "Same is Vibrato Fanin, accept cmd",
  "increases the vibrato speed.",
  "",
  "",
  "",

  "  G 00000000  (Volume System)",
  "    |Sp||Rt|",
  "",
  "Slide's volume of the chan upwards. Is",
  "dual parameter cmd. 'Sp' is the speed",
  "(the smaller the faster). 'Rt' is rate",
  "(the bigger the slower). Cmd will stop",
  "when volume FFh has been reached.",

  "  H 00000000  (Volume System)",
  "    |Sp||Rt|",
  "",
  "Same as Volume Slide Up (Cmd G) accept",
  "that it slides the volume down.",
  "",
  "",
  "",

  "  I 00000000  (Volume System)",
  "    |Sp||Rt|",
  "",
  "Will slide volume from the current",
  "vol to the sample volume specified",
  "by the slot. Params of cmd are same",
  "as Vol Slide up & Slide Dn.",
  "",

  "  J 00000000  (Volume System)",
  "    |Sp||Rt|",
  "",
  "Simulates echo effect. Will slide vol",
  "according to 'Rt', over period of tme.",
  "'Sp' sets time period. When period has",
  "expired vol is set to the original vol",
  "minus funny inverted speed calc.",

  "  K 00000000  (Volume System)",
  "    |Sp||Ap|",
  "",
  "Same is as Cmd D (Vibrato), accept",
  "it vibrates vol instead. Subject to",
  "waveforms (See 'O0' cmds).",
  "",
  "",

  "  L 00000000  (Note System)",
  "    |N1||N2|",
  "",
  "Same as ProTrack. Simulates a chorus",
  "effect, by switching the freq of a",
  "chan between three different notes-",
  "Slot note, N1 & N2. You can change the",
  "speed of arpeggio (See sam attribs).",

  "  M 00000000  (Control System)",
  "    | offs |",
  "",
  "Sets start offs of sample in a 'Full'",
  "'RLO', or 'Sam Only' slot. 'offs' is",
  "factor that determines offs. Factor is",
  "calc: = offs * (2 << OFFS SHIFTER)",
  "OFFS SHIFTER stored as sample attrib",

  "  N 00000000  (Volume System)",
  "    | Vol  |",
  "",
  "Sets the volume of a given channel.",
  "'Vol' can range from 0 to FF (or 0 to",
  "255 in decimal).", 
  "",
  "",

  "(Control System)",
  "",
  "Overrides sample attribute vibrato",
  "waveform setting, and sets channel",
  "waveform to sine wave.",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Ditto triangle wave.",
  "",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Ditto square wave.",
  "",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Ditto sawtooth wave.",
  "",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Ditto random wave.",
  "",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Overrides sample attribute tremolo",
  "waveform setting, and sets channel",
  "waveform to sine wave.",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Ditto triangle wave.",
  "",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Ditto square wave.",
  "",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Ditto sawtooth wave.",
  "",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Ditto random wave.",
  "",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Halts any 'note system' cmds that are",
  "currently running in a given channel.",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Halts any 'vol system' cmds that are",
  "currently running in a given channel.",
  "",
  "",
  "",
  "",

  "(Control System)",
  "",
  "Halts all rapid cmds that are",
  "currently running in a given channel.",
  "",
  "",
  "",
  "",

  "(Control System)",
  "NOT USED",
  "",
  "In DOS32 Funktracker, this cmd used",
  "to invert the DAC funk ctrl byte to",
  "toggle looping of a playing sample.",
  "Quite dangerous, would cause old",
  "DOS32 tracker to trash & burn.",

  "(Volume System)",
  "",
  "Performs fine balance slide leftwards.",
  "Set and forget. It will stop sliding",
  "when reached leftmost.",
  "",
  "",
  "",

  "(Volume System)",
  "",
  "Performs fine balance slide rightwrds.",
  "Set and forget. It will stop sliding",
  "when reached rightmost.",
  "",
  "",
  "",

  "  O1 0000  (Volume System)",
  "     |Sp|",
  "",
  "Sets volume to zero after given period",
  "of time. Time period determined by",
  "'Sp' (the smaller the faster).",
  "",
  "",

  "  O2 0000  (Control System)",
  "     |Sp|",
  "",
  "Will delay a slot note by a period of",
  "time. Smaller the value, the faster",
  "delay.",
  "",
  "",

  "  O3 0000  (Control System)",
  "     |Sp|",
  "",
  "Overrides speed of the arpeggio cmd",
  "(Cmd L). Changes speed of arpeggio",
  "'on the fly' after a sample has been",
  "played.",
  "",

  "  O4 0000  (Note System)",
  "     |Rt|",
  "",
  "Slides the freq of a channel upwards.",
  "Will only do it once per slot. Bigger",
  "the 'Rt' the faster the slide.",
  "",
  "",

  "  O5 0000  (Note System)",
  "     |Rt|",
  "",
  "Same as Cmd '04' accept it fine ports",
  "the frequency of a channel downwards.",
  "",
  "",
  "",

  "  O6 0000  (Volume System)",
  "     |Rt|",
  "",
  "Slides the volume level of a channel",
  "upwards. Will do it once per slot.",
  "Bigger the 'Rt' the faster the slide.",
  "",
  "",

  "  O7 0000  (Volume System)",
  "     |Rt|",
  "",
  "Same as Cmd '06' accept that it fine",
  "slides volume of a channel downwards.",
  "",
  "",
  "",

  "  O8 0000  (Volume System)",
  "     |Sp|",
  "",
  "Simulates attack/decay/sustain/release",
  "volume levels of a typical synthesised",
  "sample. Speed of crest is determined",
  "by 'Sp'.",
  "",

  "  O9 0000  (Control System)",
  "     |Gn|",
  "",
  "The DAC digital mixxing routines will",
  "take a component sample and add it to",
  "the echoing buffer of that channel.",
  "The higher 'Gn', the bigger the gain.",
  "",

  "  OA 0000  (Control System)",
  "     ||R x 4|",
  "     D",
  "Effects all channel volumes. Used to",
  "fade song volume up or down. Master",
  "volume can range from 0 to 255. Too",
  "complicated to explain here.",
  "See 'README.FunktrackerGOLD' Specs.",

  "  OB 0000  (Control System)",
  "     |Dy|",
  "",
  "Sets the echo delay of a channel. The",
  "greater the 'Dy', the larger the echo",
  "delay.",
  "",
  "",

  "  OC 0000  (Control System)",
  "     |Dc|",
  "",
  "This command sets the echo decay. The",
  "larger 'Dc', the more louder/intense",
  "the echo will be.",
  "",
  "",

  "  OD 0000  (Control System)",
  "     |Sp|",
  "",
  "Will repeately replay the sample, over",
  "and over again. 'Sp' is the speed in",
  "which it does this. Number of times",
  "it will replay the sample is determned",
  "by a setting in sample attributes.",

  "  OE 0000  (Control System)",
  "     |Bl|x16",
  "",
  "Sets pann position of a slot. Cmd is",
  "multiplied by 16 to get the actual pan",
  "value.",
  "",
  "",

  "  OF 0000  (Control System)",
  "     |Sp|",
  "",
  "Sets the speed in which the tracker",
  "'tracks' (ie the playing speed). The",
  "lower 'S[', the faster the track",
  "speed.",
  ""
};

#define PE_CMD_LIST 45
int pe_cmd_aider_hl_pos_old = 0;
int pe_cmd_aider_hl_pos = 0;
int pe_cmd_aider_real_pos = 0;

void pe_cmd_aider_dis_val(void)
{
  register int pos = pe_cmd_aider_real_pos + pe_cmd_aider_hl_pos;
  register x,y;

  attroff(A_REVERSE);
  move(8,48);
  if(pos < 14)
    addstr("00 ");
  else if(pos < 30)
    addstr("N/A");
  else
    addstr("0  ");

  for(x = 0;x < 8;x++)
  {
    register int r = (pos << 3) + x;

    move(12 + x,41);
    addstr(pe_cmd_list_des[r]);
    for(y = 0;y < (38 - strlen(pe_cmd_list_des[r]));y++) addch(' ');
  }
}

void pe_cmd_aider_dis_entry(int x)
{
  register int y,z;

  if(pe_cmd_aider_hl_pos == x)
    attron(A_REVERSE);
  else
    attroff(A_REVERSE);
  move(x + 4,1);
  z = x + pe_cmd_aider_real_pos;
  addstr(pe_cmd_list[z]);
  for(y = 0;y < (36 - strlen(pe_cmd_list[z]));y++) addch(' ');
}

void pe_cmd_aider_dis_all(void)
{
  register int x;

  for(x = 0;x < (maxy - 8);x++) pe_cmd_aider_dis_entry(x);
  pe_cmd_aider_dis_val();
}

void pe_cmd_aider_move_hl(void)
{
  attroff(A_REVERSE);
  pe_cmd_aider_dis_entry(pe_cmd_aider_hl_pos_old);
  attron(A_REVERSE);
  pe_cmd_aider_dis_entry(pe_cmd_aider_hl_pos);
  pe_cmd_aider_hl_pos_old = pe_cmd_aider_hl_pos;
  pe_cmd_aider_dis_val();
}

void pe_cmd_aider_up(void)
{
  if(pe_cmd_aider_hl_pos > 0)
  {
    pe_cmd_aider_hl_pos--;
    pe_cmd_aider_move_hl();
    update_screen();
  }
  else
  {
    if(pe_cmd_aider_real_pos > 0) pe_cmd_aider_real_pos--;
    pe_cmd_aider_dis_all();
    update_screen();
  }
}

void pe_cmd_aider_dn(void)
{
  if(pe_cmd_aider_hl_pos < (maxy - 9))
  {
    pe_cmd_aider_hl_pos++;
    pe_cmd_aider_move_hl();
    update_screen();
  }
  else
  {
    if(pe_cmd_aider_real_pos < ((PE_CMD_LIST + 8) - maxy))
      pe_cmd_aider_real_pos++;
    pe_cmd_aider_dis_all();
    update_screen();
  }
}

void pe_cmd_aider_bg(void)
{
  register int x,option = 1;
  register amaxy = maxy - 4;
  char str[] = "   ";

  display_topbar();
  attroff(A_REVERSE);
  move(1,0);
  addstr("Command Assistant (dumb name?)");
  move(4,40);
  addstr("Hit enter to select command, then enter");
  move(5,40);
  addstr("command value if applicable.");
  attron(A_REVERSE);
  for(x = 0;x < (maxy - 7);x++)
  {
    move(x + 4,0);
    addch(' ');
    move(x + 4,37);
    addch(' ');
  }
  clear_area(3,0,3,37);
  clear_area(amaxy,0,amaxy,37);
  clear_area(7,40,9,52);
  for(x = 0;x < 10;x++)
  {
    move(11 + x,40);
    addch(' ');
    move(11 + x,79);
    addch(' ');
  }
  clear_area(11,41,11,78);
  clear_area(20,41,20,78);

  attroff(A_REVERSE);
  move(8,41);
  addstr("Value:    ");

  pe_cmd_aider_dis_all();
  update_screen();
  while(option)
  {
    ch = getch();
    switch(ch)
    {
      case FC_SCREEN_ESCAPE: /*quit assistant*/
        option = 0;
        break;
      case FC_ARROW_UP:
        pe_cmd_aider_up();
        break;
      case FC_ARROW_DN:
        pe_cmd_aider_dn();
        break;
      case FC_ENTER:
        {
          register int pos = pe_cmd_aider_real_pos + pe_cmd_aider_hl_pos;
          register int command = 0xf,com_val = 0;
          calc_slot

          if(pos < 14)
          {
            get_string(8,48,str,2);
            command = pos;
            com_val = hex2char(str);
            if(com_val == -1) com_val = 0;
          }
          else if(pos < 30)
          {
            command = 0xe;
            com_val = (pos - 14) & 0xf;
          }
          else
          {
            get_string(8,48,str,1);
            command = 0xe;
            com_val = (((pos - 29) & 0xf) << 4) + (hex2nib(str) & 0xf);
          }
          pe_slot->sam_com &= 0xf0;
          pe_slot->sam_com |= command;
          pe_slot->com_val = com_val;
        }
        update_screen();
        return;
    }
  }
}

/***************************************************************************
*
***************************************************************************/
void pe_main(void)
{
  register int nv;

  switch(ch)
  {
    case FC_GOTO_SAM_EDIT:
      edit_mode = EM_SAMPLE;
      main_editor_dis_bg();
      break;
    case FC_GOTO_SEQ_EDIT:
      edit_mode = EM_SEQUENCE;
      main_editor_dis_bg();
      break;
    case FC_SCREEN_ESCAPE:
      if(quit_confirm()) edit_mode = EM_MAIN;
      break;
    case FC_TRAC_PAT:
      play_song(0);
      main_editor_dis_bg();
      break;
    case FC_TRAC_DGB:
      play_song(1);
      main_editor_dis_bg();
      break;
    case FC_TRAC_PTY:
      play_song(2);
      main_editor_dis_bg();
      break;
    case FC_SAVE_SONG:
      save_song(1);
      break;
    case FC_SAVE_MOD:
      save_song(0);
      break;
    case FC_PE_DEC_PAT_NO:
      if(pe_pattern_no > 0) pe_pattern_no--;
      pe_display_all();
      update_screen();
      break;
    case FC_PE_INC_PAT_NO:
      if(pe_pattern_no < (0x80 - 1)) pe_pattern_no++;
      pe_display_all();
      update_screen();
      break;
    case FC_PE_DEC_SAM_NO:
      pe_dec_sam();
      break;
    case FC_PE_INC_SAM_NO:
      pe_inc_sam();
      break;
    case FC_DEC_OCT:
      dec_oct_no();
      break;
    case FC_INC_OCT:
      inc_oct_no();
      break;
    case FC_PE_CLR_SLOT:
      pe_clr_slot();
      break;
    case FC_PE_SET_BLK:
      funk_hr_ptr->break_list[pe_pattern_no] = pe_pos_real + pe_pos_hl;
      pe_display_ind(pe_pattern_no,pe_pos_real);
      update_screen();
      break;
    case FC_PE_ED_SAM_NO:
      pe_edit_slot_sam();
      update_screen();
      break;
    case FC_PE_ED_CMD:
      pe_edit_slot_com();
      update_screen();
      break;
    case FC_PE_ADD_RLO:
      pe_enter_RLO();
      update_screen();
      break;
    case FC_INS_ENTRY:
      pe_insert_slot();
      break;
    case FC_DEL_ENTRY:
      pe_delete_slot();
      break;
    case FC_ARROW_UP:
      pe_cursor_up();
      break;
    case FC_ARROW_DN:
      pe_cursor_down();
      break;
    case FC_ARROW_LEFT:
      pe_cursor_left();
      break;
    case FC_ARROW_RIGHT:
      pe_cursor_right();
      break;
    case FC_PE_MBEGIN:
      pe_begin_mark();
      break;
    case FC_PE_MEND:
      pe_end_mark();
      break;
    case FC_PE_MCOPY:
      pe_copy_mark();
      break;
    case FC_PE_MDELETE:
      pe_delete_mark();
      break;
    case FC_PE_MPASTE:
      pe_paste_mark();
      break;
    case FC_PE_CHANTOGG:
      funk_chan[pe_chan_real + pe_chan_hl].channel_kill ^= 1;
      pe_dischan_bg();
      update_screen();
      break;
    case FC_PE_CMD_ASSIST:
      pe_cmd_aider_bg();
      main_editor_dis_bg();
      break;
  }
  if((nv = mus_kb_input()) != -1)
  {
    pe_note_no = nv;
    pe_enter_slot();
    play_slot(nv + (pe_octave_no * 12));
  }
}

