/* FMMOD FM MIDI Sound Module, plays the fm chip with MIDI messages from MIDI IN */
/* With Built-in editor                                  */
/* Last revision: 941017   */

#include <stdio.h>
#include <sbc.h>
#include <sbcmusic.h>
#include <bios.h>
#include <conio.h>
#include <dos.h>
#include <dir.h>
#include <cursor.h>

#include <midi.h>
#include <mkbrk.h>

#define SbProID 100
#define KeyUp 0x80

int edpatch;
int opr;
int Loaded_before;
char curr_cmf[80];
char cmfname[80];

unsigned char out_cnvrt(int i,int param);

unsigned char in_runstatus,out_runstatus;

char channel_on[16];

char default_instr[16] =
               {   0x021,0x011,0x04C,0x000,0x0F1,0x0F2,0x063,0x072,
                       0x000,0x000,0x004,0x000,0x000,0x000,0x000,0x000 };

char InstrBuf[128][16];

int numinstr;

unsigned char old_note,velocity,octave,program;

int editmode;
int playmode;


main(int argc,char **argv)
{
unsigned char midibyte,note,velocity,channel,program;
long midiin;
int event_flags;

       if(init(argc,argv[1]))
       {
               while(1)
               {
                       if (midiin=sbmidi_get())
                       {
                               midibyte=(unsigned char)midiin;

                               if(midibyte >= NoteOff1)
                               {
                                       if(midibyte == SysEx)
                                               sysex_rcv();
                                       else
                                       if(midibyte <= EOX)     /* Ignore System Realtime Msgs */
                                               in_runstatus = midibyte;
                               }
                               else
                               {
                                       channel = in_runstatus & 0xF;

                                       if((in_runstatus >= NoteOn1) && (in_runstatus <= NoteOn16))
                                       {
                                               do
                                                       while(!(midiin=sbmidi_get()));
                                               while((velocity=(unsigned char)midiin) > 127);  /* Ignore Non-Data msgs */

                                               if(channel_on[channel])
                                                       fmplay(channel,midibyte,velocity);
                                       }
                                       else
                                       if((in_runstatus >= ProgramChange1) && (in_runstatus <= ProgramChange16))
                                       {
                                                       program = midibyte;
                                                       sbfd_program_change(channel,program);
                                       }
                               }
                       }
                       if(playmode)
                               process_playkey();
                       else
                         if(kbhit())
                                process_key();

               }
       }
}

int init(int argc,char *argstr)
{
int i,j;
FILE* f;

/*
       if(argc > 0)
       {
*/
               if ( ! GetEnvSetting() )
               {
                       if (sbc_check_card() & 4)
                       {
                               if (sbc_test_int())
                               {
                                       sbfd_init();
                                       sbfd_setmode(1);

                                       sbfd_program_change(15,15);

                                       sbmidi_init(ct_int_num);

                                       velocity = 64;
                                       octave = 5;
                                       program = 0;

                                       editmode = 0;
                                       playmode = 0;
                                       edpatch = 0;

                                       for(i=0;i<16;i++)
                                               channel_on[i] = 1;

                                       for(i=0;i<128;i++)
                                               for(j=0;j<16;j++)
                                                       InstrBuf[i][j]=default_instr[j];

                                       if(f=fopen("fmmod.cfg","rb"))
                                               get_config_settings(f);


                                       if(f=fopen("default.set","rb"))
                                       {
                                               fclose(f);
                                               loadpatches2("default.set");
                                       }

                                       sbfd_instrument((char far*)InstrBuf);
                                       draw_screen();
                               }
                               else
                               {
                                       printf("Error on interrupt.\n");
                                       return(0);
                               }
                       }
                       else
                       {
                               printf("Sound Blaster Card not found or wrong I/O setting.\n") ;
                               return(0);
                       }
               }
               else
               {
                       printf("BLASTER environment variable not set or incomplete or invalid.\n");
                       return(0);
               }
               return(1);

/*
       }
       else
       {
               printf("FMMOD FM MIDI Sound Module v1.0\n");
               printf("usage: fmmod <irq>\n");
               return(0);
       }
*/
}

quit()
{
int k;

       gotoxy(1,22);
       printf("Do you really want to quit (Y\\n)?");

       k = 0;

       while(1)
        if(kbhit()) break;

       if(kbhit())
               k = (bioskey(0) >> 8) & 0xff;

       if( k == 28 || k == 21 )
       {
               sbmidi_exit();
               sbfd_music_off();
               sbfd_reset();
               showcur();
               clrscr();
               exit(0);
       }
       draw_screen();
}



get_config_settings(FILE* cfgf)
{
       fread(&channel_on[0],1,16,cfgf);
       fread(&velocity,1,1,cfgf);
       fclose(cfgf);
}

save_config_settings()
{
FILE* cfgf;
       cfgf = fopen("fmmod.cfg","wb");
       fwrite(&channel_on[0],1,16,cfgf);
       fwrite(&velocity,1,1,cfgf);
       fclose(cfgf);
}

draw_screen()
{
int i;
       clrscr();
       printf("*** FM MIDI Sound Module v1.0 ***                     Current Program: %-3u\n\n",program);
       printf("Alt-V Set Velocity  Alt-O All notes off  Alt-Y FM Editor  Alt-Z List Files\n\n");
       printf("Alt-P Load Patch  Alt-L Load 128-patch set  Alt-S Save 128-patch set\n\n");
       printf("Alt-W Save Settings  Alt-U SysEx Xmit\n\n");
       printf("P Play Keyboard  <ESC> Quit\n\n\n");

       printf("Channels enabled/disabled\n");
       printf("-------------------------\n\n");
       printf("0 1 2 3 4 5 6 7 8 9 A B C D E F\n\n");

       for(i=0;i<16;i++)
               printf("%X ",channel_on[i]);
}


process_key()
{
int scan,modifiers;

       scan = (bioskey(0) >> 8) & 0xFF;
       modifiers = bioskey(2);

       if(modifiers & 8)
       {
               switch(scan) {
                       case 47 : setveloc();break;        /* Alt-V */
                       case 24 : all_notes_off();break;   /* Alt-O */
                       case 25 : loadpatch();break;       /* Alt-P */
                       case 38 : loadpatches();break;     /* Alt-L */
                       case 31 : savepatches();break;     /* Alt-S */
                       case 44 : listfiles();break;       /* Alt-Z */
                       case 17 : save_config_settings();break; /* Alt-W */
                       case 21 : fm_edit();break;          /* Alt-Y */
                       case 22 : sysex_xmit();break;       /* Alt-U */

                       case 120 : channel_on[1] = channel_on[1] ^ 1; break;
                       case 121 : channel_on[2] = channel_on[2] ^ 1; break;
                       case 122 : channel_on[3] = channel_on[3] ^ 1; break;
                       case 123 : channel_on[4] = channel_on[4] ^ 1; break;
                       case 124 : channel_on[5] = channel_on[5] ^ 1; break;
                       case 125 : channel_on[6] = channel_on[6] ^ 1; break;
                       case 126 : channel_on[7] = channel_on[7] ^ 1; break;
                       case 127 : channel_on[8] = channel_on[8] ^ 1; break;
                       case 128 : channel_on[9] = channel_on[9] ^ 1; break;
                       case 129 : channel_on[0] = channel_on[0] ^ 1; break;
                       case 30 : channel_on[10] = channel_on[10] ^ 1; break;
                       case 48 : channel_on[11] = channel_on[11] ^ 1; break;
                       case 46 : channel_on[12] = channel_on[12] ^ 1; break;
                       case 32 : channel_on[13] = channel_on[13] ^ 1; break;
                       case 18 : channel_on[14] = channel_on[14] ^ 1; break;
                       case 33 : channel_on[15] = channel_on[15] ^ 1; break;
               }
               draw_screen();
       }
       else
       {
               switch(scan){
                       case 25 : goto_playmode(); break;
                       case 1 :  quit();
               }
       }
}


unsigned char old_key,old_release;

process_playkey()
{
unsigned char key;


       key=get_mkbrk_code();

       if(key != old_key)
       {
               if(key >= KeyUp)
               {
                       if((key-0x80) == old_key)
                       {
                               stop_note();
                               old_key = key;
                       }
               }
               else
               {
                       if(old_key < KeyUp)
                               stop_note();

                       switch(key) {
                               case 44 : playnote(0);break;                /* z */
                               case 31 : playnote(1);break;                /* s */
                               case 45 : playnote(2);break;                /* x */
                               case 32 : playnote(3);break;               /* d */
                               case 46 : playnote(4);break;               /* c */
                               case 47 : playnote(5);break;               /* v */
                               case 34 : playnote(6);break;               /* g */
                               case 48 : playnote(7);break;               /* b */
                               case 35 : playnote(8);break;               /* h */
                               case 49 : playnote(9);break;               /* n */
                               case 36 : playnote(10);break;               /* j */
                               case 50 : playnote(11);break;               /* m */
                               case 51 : playnote(12);break;               /* , */

                               case 16 : playnote(12);break;               /* q */
                               case 3  : playnote(13);break;              /* 2 */
                               case 17 : playnote(14);break;             /* w */
                               case 4  : playnote(15);break;             /* 3 */
                               case 18 : playnote(16);break;             /* e */
                               case 19 : playnote(17);break;              /* f */
                               case 6  : playnote(18);break;             /* 5 */
                               case 20 : playnote(19);break;             /* t */
                               case 7  : playnote(20);break;             /* 6 */
                               case 21 : playnote(21);break;            /* y */
                               case 8  : playnote(22);break;           /* 7 */
                               case 22 : playnote(23);break;           /* u */
                               case 23 : playnote(24);break;           /* i */

                               case 78 : if(octave < 7) octave++; break;
                               case 74 : if(octave > 2) octave--; break;

                               case 73 : change_program(key,0); break;
                               case 81 : change_program(key,0);break;  /* PgDwn */

                               case  1 : exit_playmode();
                       }
                       old_key = key;
               }
       }
}

goto_playmode()
{
       init_mkbrk();
       playmode = 1;
       gotoxy(1,22);
       printf("Keyboard Play mode");

}

exit_playmode()
{
       while(get_mkbrk_code() != 0x81);
       exit_mkbrk();
       playmode = 0;
       gotoxy(1,22);
       printf("                  ");
}


loadpatch()
{
FILE* f;

char patchname[80],patchnumstr[10];

       clrscr();
       showcur();

       printf("Name of single patch to load: ");
       gets(patchname);

       if (strlen(patchname) != 0)
       {
               printf("Patch number to load patch into (0-127): ");
               gets(patchnumstr);

               if(f=fopen(patchname,"rb"))
               {
                       fread(&InstrBuf[atoi(patchnumstr)][0],16,1,f);
                       fclose(f);
                       sbfd_program_change(0,atoi(patchnumstr));
               }
               else
               {
                       printf("Couldn't find %s!\n",patchname);
                       delay(1000);
               }
       }
       if(editmode)
               draw_editscreen();
       else
               draw_screen();
}


loadpatches()
{
char patchsetname[80];

       clrscr();
       showcur();
       printf("Name of patch set to load: ");
       gets(patchsetname);

       loadpatches2(patchsetname);

       if(editmode)
               draw_editscreen();
       else
               draw_screen();
}

loadpatches2(char *patchfile)
{
FILE* f;
int i;

       if(f = fopen(patchfile,"rb"))
       {
               for(i=0;i<128;i++)                     /* read instruments */
                       fread(&InstrBuf[i][0],1,16,f);
               fclose(f);
       }
       else
       {
               printf("Couldn't find %s!\n",patchfile);
               delay(1000);
       }
}

savepatches()
{
FILE* f;
int i;
char patchsetname[80];

       clrscr();
       showcur();
       printf("Save patch set as: ");
       gets(patchsetname);

       f = fopen(patchsetname,"wb");

       for(i=0;i<128;i++)
               fwrite(&InstrBuf[i][0],1,16,f);

       fclose(f);

       if(editmode)
               draw_editscreen();
       else
               draw_screen();
}

listfiles()
{
struct ffblk ffblk;
int done;

       clrscr();

       system("dir /w /p /on");

       printf("\nPress any key to return to FMMOD...\n");
       getch();

       if(editmode)
               draw_editscreen();
       else
               draw_screen();
}


fmplay(unsigned char channel, unsigned char note, unsigned char velocity)
{
       if(velocity != 0)
          sbfd_note_on(channel,note,velocity);
       else
               sbfd_note_off(channel,note,velocity);
}

setveloc()
{
char velocstr[10];
       showcur();
       gotoxy(1,22);
       printf("\nSet velocity (0-127): ");
       gets(velocstr);
       velocity = atoi(velocstr);
       hidecur();
}

all_notes_off()
{
       sbfd_music_off();
}


playnote(unsigned char offs)
{
unsigned char note;

       note = (octave*12)+offs;

       if(out_runstatus != 0x90)
       {
               out_runstatus = 0x90;
               sbmidi_put(0x90);                          /* Note On channel 1 */
       }

       sbmidi_put(note);
       sbmidi_put(velocity);

       old_note = note;
}

stop_note()
{
       if(out_runstatus != 0x90)
       {
               out_runstatus = 0x90;
               sbmidi_put(0x90);
       }
       sbmidi_put(old_note);
       sbmidi_put(0);
}

change_program(int scan,int button)
{
       if(! button)
       {
               switch(scan){
                       case 73 : {
                               if(program < 127)
                                       program++;
                       }break;
                       case 81 : {
                               if(program > 0)
                                       program--;
                       }break;
               }
               gotoxy(65,1);printf("%-3u",program);
               gotoxy(65,1);
       }
       else
       {
/*
               switch(button){

                       case 1 : chg_while_pressed(1,0,&program,65,1,"%-3u"); break;
                       case 2 : chg_while_pressed(2,127,&program,65,1,"%-3u");
               }
*/
       }

       if(out_runstatus != ProgramChange1)
       {
               out_runstatus = ProgramChange1;
               sbmidi_put(ProgramChange1);
       }
       sbmidi_put(program);
}


sysex_rcv()
{
unsigned char param,val;
int i,j,offs;
long midiin;
unsigned int size,segp;
unsigned char sysdata[6700],sysexbyte;

               offs=0;

               while(1)
               {
                       if(midiin=sbmidi_get())
                       {
                               sysexbyte=(unsigned char)midiin;
                               sysdata[offs++]=sysexbyte;
                       }
                       if(sysexbyte == EOX) break;
               }

               offs=0;

               gotoxy(1,22);

               if(sysdata[offs++] == SbProID)                      /* Check for SBPro ID */
               {
                       switch(sysdata[offs++]){                   /* get mode */
                               case 0 :                               /* All patches */
                               {
                                       for(i=0;i<128;i++)
                                       {
                                               for(j=0;j<26;j++)
                                               {
                                                       param=sysdata[offs++];
                                                       val=sysdata[offs++];
                                                       setFMparam(i,param,val);
                                               }
                                       }
                               }  break;
                               case 1 :                            /* single patch */
                               {
                                       for(i=0;i<26;i++)
                                       {
                                               param=sysdata[offs++];
                                               val=sysdata[offs++];
                                               setFMparam(program,param,val);
                                       }
                                       sbfd_program_change(0,0);
                               } break;
                               case 2 :                           /* Single parameter */
                               {
                                       param=sysdata[offs++];
                                       val=sysdata[offs++];
                                       setFMparam(program,param,val);
                                       sbfd_program_change(0,0);
                               }
                       }

                       if(sysdata[offs] != EOX){
                               printf("\nError on SysEx receive!\n");
                               delay(1000);
                               draw_screen();
                       }
               }
               else
               {
                       printf("\nError: not SbPro ID!\n");
                       delay(1000);
                       draw_screen();
               }
}


sysex_xmit()
{
int scan,i,j;
char patchstr[20];
int patch;

       clrscr();
       showcur();
       printf("Dump All or Single patch? (A/S)");
       do
         scan=(bioskey(0) >> 8) & 0xff;
       while( (scan != 30) && (scan != 31) );

       sbmidi_put(SysEx);
       sbmidi_put(SbProID);                   /* SbPro ID */

       switch(scan){
               case 30 :                           /* All */
                       {
                          sbmidi_put(0);
                          for(i=0;i<128;i++)
                                 for(j=0;j<26;j++)
                                 {
                                       sbmidi_put(j);
                                       sbmidi_put(out_cnvrt(i,j));
                                 }
                       }break;
               case 31 :                           /* Single patch */
                       {
                          printf("\nSelect patch to dump (0-127): ");
                          gets(patchstr);
                          patch = atoi(patchstr);

                          sbmidi_put(1);

                          for(i=0;i<26;i++)
                          {
                                       sbmidi_put(i);
                                       sbmidi_put(out_cnvrt(patch,i));
                          }
                       }
       }
       sbmidi_put(EOX);
       printf("\n\nTransmission completed.\n");
       delay(1000);
       draw_screen();
}

unsigned char out_cnvrt(int i,int param)
{
unsigned char temp,outval;

 switch(param){
        case 0 : outval = (InstrBuf[i][0] & 128) >> 7; break;
        case 1 : outval = (InstrBuf[i][0] & 64) >> 6; break;
        case 2 : outval = (InstrBuf[i][0] & 32) >> 5; break;
        case 3 : outval = (InstrBuf[i][0] & 16) >> 4; break;
        case 4 : outval = InstrBuf[i][0] & 0x0F; break;

        case 5 : outval = (InstrBuf[i][1] & 128) >> 7; break;
        case 6 : outval = (InstrBuf[i][1] & 64) >> 6; break;
        case 7 : outval = (InstrBuf[i][1] & 32) >> 5; break;
        case 8 : outval = (InstrBuf[i][1] & 16) >> 4; break;
        case 9 : outval = InstrBuf[i][1] & 0x0F; break;

        case 10 : {
                                       temp = InstrBuf[i][2] & 0xC0;

                                       switch(temp){
                                               case 0 : temp = 0;break;
                                               case 0x80 : temp = 1;break;
                                               case 0x40 : temp = 2;break;
                                               case 0xC0 : temp = 3;
                                       }

                                       outval = temp;
                               } break;
        case 11 :  outval = InstrBuf[i][2] & 0x3F; break;

        case 12 : {
                                       temp = InstrBuf[i][3] & 0xC0;

                                       switch(temp){
                                               case 0 : temp = 0;break;
                                               case 0x80 : temp = 1;break;
                                               case 0x40 : temp = 2;break;
                                               case 0xC0 : temp = 3;
                                       }

                                       outval = temp;
                               }break;
        case 13 :  outval = InstrBuf[i][3] & 0x3F; break;

        case 14 : outval = (InstrBuf[i][4] & 0xF0) >> 4; break;
        case 15 : outval = (InstrBuf[i][4] & 0x0F); break;
        case 16 : outval = (InstrBuf[i][5] & 0xF0) >> 4; break;
        case 17 : outval = (InstrBuf[i][5] & 0x0F);  break;

        case 18 : outval = (InstrBuf[i][6] & 0xF0) >> 4; break;
        case 19 : outval = InstrBuf[edpatch][6] & 0x0F; break;
        case 20 : outval = (InstrBuf[i][7] & 0xF0) >> 4; break;
        case 21 : outval = InstrBuf[edpatch][7] & 0x0F; break;

        case 22 : outval = InstrBuf[i][8]; break;
        case 23 : outval = InstrBuf[i][9]; break;

        case 24 : outval = (InstrBuf[i][10] & 0xE) >> 1; break;
        case 25 : outval = InstrBuf[i][10] & 1; break;
 }
 return(outval);
}

/*
chg_while_pressed(int button,unsigned char limit,unsigned char *chgvar,int x,int y,char *format)
{
       if(button == 1)
       {
               if((*chgvar) > limit)
                       (*chgvar)--;
               gotoxy(x,y);printf(format,*chgvar);
               gotoxy(x,y);
               delay(250);
               while(get_button_status() == button)
               {
                       if((*chgvar) > limit)
                       {
                               (*chgvar)--;
                               gotoxy(x,y);printf(format,*chgvar);
                               gotoxy(x,y);
                               delay(35);
                       }
               }
       }
       else
       {
               if((*chgvar) < limit)
                       (*chgvar)++;
               gotoxy(x,y);printf(format,*chgvar);
               gotoxy(x,y);
               delay(250);
               while(get_button_status() == button)
               {
                       if((*chgvar) < limit)
                       {
                               (*chgvar)++;
                               gotoxy(x,y);printf(format,*chgvar);
                               gotoxy(x,y);
                               delay(35);
                       }
               }
       }
}

*/

/*********************************************************************/
/*************************    FM editor functions ********************/
/*********************************************************************/


unsigned char undo_buf[16],compare_buf[16];
int compare;


void ReadVal(int *v, int llim, int ulim, int len, int x, int y)
{
char str[80];

        str[0] = len+1;
        do{
                while (! kbhit());
                if((bioskey(1) & 0xFF) == 13) break;
                gotoxy(x,y); printf("  ");
                gotoxy(x,y);
                 *v = atoi(gets(str));
                 gotoxy(x,y); printf("  ");
                 gotoxy(x,y);
        } while((*v < llim) || (*v > ulim));
}


Edit_Play()
{
unsigned char key;

       init_mkbrk();

       gotoxy(65,1); printf("PLAY MODE     ");

       do
       {
               key=get_mkbrk_code();

               if(key != old_key)
               {
                       if(key >= KeyUp)
                       {
                               if((key-0x80) == old_key)
                               {
                                       edit_stopnote();
                                       old_key = key;
                               }
                       }
                       else
                       {
                               if(old_key < KeyUp)
                                       edit_stopnote();

                               switch(key) {
                                       case 44 : edit_playnote(0);break;                /* z */
                                       case 31 : edit_playnote(1);break;                /* s */
                                       case 45 : edit_playnote(2);break;                /* x */
                                       case 32 : edit_playnote(3);break;               /* d */
                                       case 46 : edit_playnote(4);break;               /* c */
                                       case 47 : edit_playnote(5);break;               /* v */
                                       case 34 : edit_playnote(6);break;               /* g */
                                       case 48 : edit_playnote(7);break;               /* b */
                                       case 35 : edit_playnote(8);break;               /* h */
                                       case 49 : edit_playnote(9);break;               /* n */
                                       case 36 : edit_playnote(10);break;               /* j */
                                       case 50 : edit_playnote(11);break;               /* m */
                                       case 51 : edit_playnote(12);break;               /* , */

                                       case 16 : edit_playnote(12);break;               /* q */
                                       case 3  : edit_playnote(13);break;              /* 2 */
                                       case 17 : edit_playnote(14);break;             /* w */
                                       case 4  : edit_playnote(15);break;             /* 3 */
                                       case 18 : edit_playnote(16);break;             /* e */
                                       case 19 : edit_playnote(17);break;              /* f */
                                       case 6  : edit_playnote(18);break;             /* 5 */
                                       case 20 : edit_playnote(19);break;             /* t */
                                       case 7  : edit_playnote(20);break;             /* 6 */
                                       case 21 : edit_playnote(21);break;            /* y */
                                       case 8  : edit_playnote(22);break;           /* 7 */
                                       case 22 : edit_playnote(23);break;           /* u */
                                       case 23 : edit_playnote(24);break;           /* i */

                                       case 78 : if(octave < 7) octave++; break;
                                       case 74 : if(octave > 2) octave--; break;

                               }
                               old_key = key;
                       }
               }
       } while( key != 1 );
       while(get_mkbrk_code() != 0x81);

       exit_mkbrk();
       sbfd_note_off(0,old_note,0);
       gotoxy(65,1);printf("                 ");

}

edit_playnote(int offs)
{
unsigned char note;

       note = (octave*12)+offs;

       sbfd_note_on(0,note,127);
       old_note = note;
}

edit_stopnote()
{
       sbfd_note_off(0,old_note,0);
}


void modop_msg(int x, int y)
{
int k;

       k = 0;
       gotoxy(x,y); printf("1: Modulator 2: Carrier: ");

        ReadVal(&k,1,2,1,x+25,y);
       gotoxy(x,y); printf("                              ");
       opr = (char)k;
}


void AM()
{
int k;

        showcur();
       modop_msg(25,6);

        k = 0;

       gotoxy(25,6); printf("(1) On (0) Off: ");
       printf("%d",(InstrBuf[edpatch][opr-1] & 128) >> 7);

        ReadVal(&k,0,1,1,41,6);
       gotoxy(25,6); printf("                               ");

       switch(opr){
         case 1: setFMparam(edpatch,0,k);break;
         case 2: setFMparam(edpatch,5,k);
       }
       sbfd_program_change(0,edpatch);
}


void Vib()
{
int k;

        showcur();
       modop_msg(25,7);

        k = 0;

       gotoxy(25,7); printf("(1) On (0) Off: ");
       printf("%d",(InstrBuf[edpatch][opr-1] & 64) >> 6);

        ReadVal(&k,0,1,1,41,7);
       gotoxy(25,7); printf("                               ");

       switch(opr){
         case 1: setFMparam(edpatch,1,k); break;
         case 2: setFMparam(edpatch,6,k);
       }
       sbfd_program_change(0,edpatch);
}


void EG()
{
int k;

        showcur();
       modop_msg(25,8);

        k = 0;

       gotoxy(25,8); printf("(1) Continuing (0) Diminishing: ");
       printf("%d",(InstrBuf[edpatch][opr-1] & 32) >> 5);

        ReadVal(&k,0,1,1,57,8);
       gotoxy(25,8); printf("                                      ");

       switch(opr){
        case 1: setFMparam(edpatch,2,k); break;
        case 2: setFMparam(edpatch,7,k);
       }
       sbfd_program_change(0,edpatch);
}


void KSR()
{
int k;

        showcur();
       modop_msg(25,9);

        k = 0;

       gotoxy(25,9); printf("(1) On (0) Off: ");
       printf("%d",(InstrBuf[edpatch][opr-1] & 16) >> 4);

        ReadVal(&k,0,1,1,41,9);
       gotoxy(25,9); printf("                                      ");

       switch(opr){
        case 1: setFMparam(edpatch,3,k); break;
        case 2: setFMparam(edpatch,8,k);
       }
       sbfd_program_change(0,edpatch);
}


void Multiple()
{
int val;

        showcur();
       modop_msg(25,10);

        showcur();
       gotoxy(25,10); printf("Multiple (0-15): ");
       printf("%d",InstrBuf[edpatch][opr-1] & 0x0F);

       ReadVal(&val,0,15,2,42,10);

       gotoxy(25,10); printf("                           ");

       switch(opr){
               case 1 : setFMparam(edpatch,4,val); break;
               case 2 : setFMparam(edpatch,9,val);
       }
       sbfd_program_change(0,edpatch);
}


void KSL()
{
int val;
int temp;

        showcur();
       modop_msg(25,11);

        showcur();
       gotoxy(25,11); printf("KSL value (0-3): ");

       temp = InstrBuf[edpatch][2+(opr-1)] & 0xC0;

       switch(temp){
        case 0 :    printf("%d",0);break;
        case 0x80 : printf("%d",1);break;
        case 0x40 : printf("%d",2);break;
        case 0xC0 : printf("%d",3);
       }

        ReadVal(&val,0,3,1,42,11);
       gotoxy(25,11); printf("                          ");

       switch(opr){
        case 1: setFMparam(edpatch,10,val); break;
        case 2: setFMparam(edpatch,12,val);
       }
       sbfd_program_change(0,edpatch);
}

void TotLev()
{
int val;

        showcur();
       modop_msg(25,12);

        showcur();
       gotoxy(25,12); printf("Total Level (0-63): ");
       printf("%d", InstrBuf[edpatch][2+(opr-1)] & 0x3F);

        ReadVal(&val,0,63,2,45,12);
       gotoxy(25,12); printf("                              ");

       switch(opr){
               case 1 : setFMparam(edpatch,11,val); break;
               case 2 : setFMparam(edpatch,13,val);
       }
       sbfd_program_change(0,edpatch);
}

void Attack()
{
int val;

        showcur();
       modop_msg(25,13);

        showcur();
       gotoxy(25,13); printf("Attack (0-15): ");
       printf("%d",(InstrBuf[edpatch][4+(opr-1)] & 0xF0) >> 4);

        ReadVal(&val,0,15,2,40,13);
       gotoxy(25,13); printf("                              ");

       switch(opr){
               case 1 : setFMparam(edpatch,14,val); break;
               case 2 : setFMparam(edpatch,16,val);
       }
       sbfd_program_change(0,edpatch);
}


void Decay()
{
int val;

        showcur();
       modop_msg(25,14);

        showcur();
       gotoxy(25,14); printf("Decay (0-15): ");
       printf("%d", InstrBuf[edpatch][4+(opr-1)] & 0x0F);

       ReadVal(&val,0,15,2,39,14);
       gotoxy(25,14); printf("                              ");

       switch(opr){
               case 1 : setFMparam(edpatch,15,val); break;
               case 2 : setFMparam(edpatch,17,val);
       }
       sbfd_program_change(0,edpatch);
}


void Sustain()
{
int val;

        showcur();
       modop_msg(25,15);

        showcur();
       gotoxy(25,15); printf("Sustain Level (0-15): ");
       printf("%d",(InstrBuf[edpatch][6+(opr-1)] & 0xF0) >> 4);

        ReadVal(&val,0,15,2,47,15);
       gotoxy(25,15); printf("                              ");

       switch(opr){
               case 1 : setFMparam(edpatch,18,val); break;
               case 2 : setFMparam(edpatch,20,val);
       }
       sbfd_program_change(0,edpatch);
}


void Release()
{
int val;

        showcur();
       modop_msg(25,16);

        showcur();
       gotoxy(25,16); printf("Release (0-15): ");
       printf("%d", InstrBuf[edpatch][6+(opr-1)] & 0x0F);

        ReadVal(&val,0,15,2,41,16);
        gotoxy(25,16); printf("                  ");

       switch(opr){
               case 1: setFMparam(edpatch,19,val); break;
               case 2: setFMparam(edpatch,21,val);
       }
       sbfd_program_change(0,edpatch);
}

void Wave()
{
int val;

        showcur();
       modop_msg(25,17);

       val = 0;

       gotoxy(25,17); printf("Wave (0-4): ");
       printf("%d", InstrBuf[edpatch][8+(opr-1)]);

        ReadVal(&val,0,4,1,37,17);
       gotoxy(25,17); printf("                              ");

       switch(opr){
               case 1 : setFMparam(edpatch,22,val);break;
               case 2 : setFMparam(edpatch,23,val);
       }
       sbfd_program_change(0,edpatch);
}


void Feedback()
{
int val;

       val = 0;
        showcur();

       gotoxy(25,18); printf("Feedback (0-7): ");
       printf("%d",(InstrBuf[edpatch][10] & 0xE) >> 1);

        ReadVal(&val,0,7,1,41,18);
       gotoxy(25,18); printf("                       ");

       setFMparam(edpatch,24,val);

       sbfd_program_change(0,edpatch);
}

void Connection()
{
int val;

        showcur();
       val = 0;
       gotoxy(25,19); printf("Connection (0,1): ");
       printf("%d",InstrBuf[edpatch][10] & 1);

        ReadVal(&val,0,1,1,43,19);
       gotoxy(25,19); printf("                       ");

       setFMparam(edpatch,25,val);
       sbfd_program_change(0,edpatch);
}

void savepatch()
{
FILE* f;
char scr_buf[4096];
char patchname[80];
int i,j;

       gettext(1,1,80,25,scr_buf);
       clrscr();
       showcur();

        printf("Patch file to save to: ");
        gets(patchname);

        if (f = fopen(patchname,"wb"))
        {
               fwrite(&InstrBuf[edpatch][0],1,16,f);
               fclose(f);
        }
        else
        {
                 printf("\nError opening file");
                 delay(2000);
        }
       puttext(1,1,80,25,scr_buf);
}

Select_Patch()
{
char str[20];

       clrscr();
       showcur();
       printf("Patch to edit (0-127): ");
       gets(str);
       edpatch = atoi(str);
       compare=0;
       sbfd_program_change(0,edpatch);
       save_undo_settings(edpatch);
       draw_editscreen();
}



draw_editscreen()
{
        hidecur();
        clrscr();

       printf("Patch currently selected: %d\n\n",edpatch);
       printf("Parameters\n");
       printf("----------\n\n");
       printf("1: AM On/Off\n");
       printf("2: Vib On/Off\n");
       printf("3: EG\n");
       printf("4: KSR\n");
       printf("5: Multiple\n");
       printf("6: KSL\n");
       printf("7: Total Level\n");
       printf("8: Attack\n");
       printf("9: Decay\n");
       printf("A: Sustain\n");
       printf("B: Release\n");
       printf("C: Wave\n");
       printf("D: Feedback                                     Alt-F: List Files\n");
       printf("E: Connection                                       L: Load Patch Set\n");
       printf("N: Select Patch to edit                             S: Save Patch Set\n");
       printf("P: Play Keyboard                                Alt-L: Load Patch\n");
       printf("U: Undo changes to patch                        Alt-S: Save Patch\n");
       printf("X: Compare\n");

}

fm_edit()
{
int k,mod;

       editmode = 1;
       compare=0;
       sbfd_program_change(0,edpatch);
       save_undo_settings();

       textcolor(GREEN);

       draw_editscreen();

       while((k & 0xff) != 27)
       {
                 hidecur();
                 while(! bioskey(1));
                 mod = bioskey(2);

                 k = bioskey(0);

               switch((k >> 8) & 0xFF)
               {
                       case 0x02: AM(); break;
                       case 0x03: Vib(); break;
                       case 0x04: EG(); break;
                       case 0x05: KSR(); break;
                       case 0x06: Multiple(); break;
                       case 0x07: KSL(); break;
                       case 0x08: TotLev(); break;
                       case 0x09: Attack(); break;
                       case 0x0A: Decay(); break;
                       case 0x1E: Sustain(); break;
                       case 0x30: Release(); break;
                       case 0x2e: Wave(); break;
                       case 0x20: Feedback(); break;
                       case 0x12: Connection(); break;
                       case 0x21: if(mod & 8)
                                                       listfiles();
                                               break;
                       case 0x26: if(mod & 8)
                                                       loadpatch();
                                               else loadpatches();
                                               break;

                       case 0x1F: if(mod & 8)
                                                       savepatch();
                                          else savepatches();
                                          break;

                       case 0x31: Select_Patch(); break;
                       case 0x19: Edit_Play(); break;
                       case 0x16: Undo(); break;
                       case 0x2d: Compare(); break;
               }
       }
       showcur();
       editmode = 0;
       textcolor(LIGHTGRAY);
       draw_screen();
}

save_undo_settings()
{
int i;

       for(i=0;i<16;i++)
               undo_buf[i] = InstrBuf[edpatch][i];

}

Compare()
{
int i;

       compare = compare ^ 1;

       if(compare)
       {
               for(i=0;i<16;i++)
                       compare_buf[i]=InstrBuf[edpatch][i];

               for(i=0;i<16;i++)
                       InstrBuf[edpatch][i]=undo_buf[i];

               gotoxy(46,3);printf("Compare, now original patch");
       }
       else
       {
               for(i=0;i<16;i++)
                       InstrBuf[edpatch][i]=compare_buf[i];

               gotoxy(46,3);printf("                           ");
       }
       sbfd_program_change(0,edpatch);
}


Undo()
{
int i;

       for(i=0;i<16;i++)
               InstrBuf[edpatch][i] = undo_buf[i];

       sbfd_program_change(0,edpatch);
}

setFMparam(int edpatch,int param,int val)
{
       switch(param){
               case 0 :                    /* AM Modulator */
                       {
                               switch(val){
                                       case 1: InstrBuf[edpatch][0] = InstrBuf[edpatch][0] | 0x80; break;
                                       case 0: InstrBuf[edpatch][0] = InstrBuf[edpatch][0] & 0x7F;
                               }
                       }break;
               case 1 :                    /* VIB Modulator */
                       {
                               switch(val){
                                       case 1: InstrBuf[edpatch][0] = InstrBuf[edpatch][0] | 0x40; break;
                                       case 0: InstrBuf[edpatch][0] = InstrBuf[edpatch][0] & 0xBF;
                               }

                       }break;
               case 2 :                   /* EG modulator */
                       {
                               switch(val){
                                       case 1: InstrBuf[edpatch][0] = InstrBuf[edpatch][0] | 0x20; break;
                                       case 0: InstrBuf[edpatch][0] = InstrBuf[edpatch][0] & 0xDF;
                               }
                       }break;
               case 3 :                  /* KSR modulator */
                       {
                               switch(val){
                                       case 1: InstrBuf[edpatch][0] = InstrBuf[edpatch][0] | 0x10; break;
                                       case 0: InstrBuf[edpatch][0] = InstrBuf[edpatch][0] & 0xEF;
                               }
                       } break;
               case 4 :                 /* Multiple modulator */
                       {
                               InstrBuf[edpatch][0] = InstrBuf[edpatch][0] & 0xF0;
                               InstrBuf[edpatch][0] = InstrBuf[edpatch][0] | val;
                       } break;

               case 5 :                    /* AM Carrier */
                       {
                               switch(val){
                                       case 1: InstrBuf[edpatch][1] = InstrBuf[edpatch][1] | 0x80; break;
                                       case 0: InstrBuf[edpatch][1] = InstrBuf[edpatch][1] & 0x7F;
                               }
                       } break;
               case 6 :                    /* VIB Carrier */
                       {
                               switch(val){
                                       case 1: InstrBuf[edpatch][1] = InstrBuf[edpatch][1] | 0x40; break;
                                       case 0: InstrBuf[edpatch][1] = InstrBuf[edpatch][1] & 0xBF;
                               }

                       } break;
               case 7 :                   /* EG Carrier */
                       {
                               switch(val){
                                       case 1: InstrBuf[edpatch][1] = InstrBuf[edpatch][1] | 0x20; break;
                                       case 0: InstrBuf[edpatch][1] = InstrBuf[edpatch][1] & 0xDF;
                               }
                       } break;
               case 8 :                  /* KSR Carrier */
                       {
                               switch(val){
                                       case 1: InstrBuf[edpatch][1] = InstrBuf[edpatch][1] | 0x10; break;
                                       case 0: InstrBuf[edpatch][1] = InstrBuf[edpatch][1] & 0xEF;
                               }
                       } break;
               case 9 :                 /* Multiple Carrier */
                       {
                               InstrBuf[edpatch][1] = InstrBuf[edpatch][1] & 0xF0;
                               InstrBuf[edpatch][1] = InstrBuf[edpatch][1] | val;
                       } break;

               case 10 :             /* KSL Modulator */
                       {
                               switch(val){
                                       case 0: val = 0; break;
                                       case 1: val = 0x80; break;
                                       case 2: val = 0x40; break;
                                       case 3: val = 0xC0; break;
                               }
                               InstrBuf[edpatch][2] = InstrBuf[edpatch][2] & 0x3F;
                               InstrBuf[edpatch][2] = InstrBuf[edpatch][2] | val;
                       } break;

               case 11 :             /* TL Modulator */
                       {
                               InstrBuf[edpatch][2] = InstrBuf[edpatch][2] & 0xC0;
                               InstrBuf[edpatch][2] = InstrBuf[edpatch][2] | val;
                       } break;

               case 12 :             /* KSL Carrier */
                       {
                               switch(val){
                                       case 0: val = 0; break;
                                       case 1: val = 0x80; break;
                                       case 2: val = 0x40; break;
                                       case 3: val = 0xC0; break;
                               }
                               InstrBuf[edpatch][3] = InstrBuf[edpatch][3] & 0x3F;
                               InstrBuf[edpatch][3] = InstrBuf[edpatch][3] | val;
                       } break;

               case 13 :             /* TL Carrier */
                       {
                               InstrBuf[edpatch][3] = InstrBuf[edpatch][3] & 0xC0;
                               InstrBuf[edpatch][3] = InstrBuf[edpatch][3] | val;
                       } break;

               case 14 :            /* Attack Modulator */
                       {
                               InstrBuf[edpatch][4] = InstrBuf[edpatch][4] & 0x0F;
                               InstrBuf[edpatch][4] = InstrBuf[edpatch][4] | (val << 4);
                       } break;

               case 15 :           /* Decay Modulator */
                       {
                               InstrBuf[edpatch][4] = InstrBuf[edpatch][4] & 0xF0;
                               InstrBuf[edpatch][4] = InstrBuf[edpatch][4] | val;
                       } break;


               case 16 :            /* Attack Carrier */
                       {
                               InstrBuf[edpatch][5] = InstrBuf[edpatch][5] & 0x0F;
                               InstrBuf[edpatch][5] = InstrBuf[edpatch][5] | (val << 4);
                       } break;

               case 17 :           /* Decay Carrier */
                       {
                               InstrBuf[edpatch][5] = InstrBuf[edpatch][5] & 0xF0;
                               InstrBuf[edpatch][5] = InstrBuf[edpatch][5] | val;
                       } break;
               case 18 :          /* Sustain Level Modulator */
                       {
                               InstrBuf[edpatch][6] = InstrBuf[edpatch][6] & 0x0F;
                               InstrBuf[edpatch][6] = InstrBuf[edpatch][6] | (val << 4);
                       } break;

               case 19 :         /* Release rate Modulator */
                       {
                               InstrBuf[edpatch][6] = InstrBuf[edpatch][6] & 0xF0;
                               InstrBuf[edpatch][6] = InstrBuf[edpatch][6] | val;
                       } break;

               case 20 :          /* Sustain Level Carrier */
                       {
                               InstrBuf[edpatch][7] = InstrBuf[edpatch][7] & 0x0F;
                               InstrBuf[edpatch][7] = InstrBuf[edpatch][7] | (val << 4);
                       } break;

               case 21 :         /* Release rate Carrier */
                       {
                               InstrBuf[edpatch][7] = InstrBuf[edpatch][7] & 0xF0;
                               InstrBuf[edpatch][7] = InstrBuf[edpatch][7] | val;
                       } break;


               case 22 :    /* Wave Select Modulator */
                       {
                               InstrBuf[edpatch][8] = val;
                       } break;

               case 23 :    /* Wave Select Carrier */
                       {
                               InstrBuf[edpatch][9] = val;
                       } break;

               case 24 :     /* Feedback */
                       {
                               InstrBuf[edpatch][10] = InstrBuf[edpatch][10] & 0xF1;
                               InstrBuf[edpatch][10] = InstrBuf[edpatch][10] | ( ((char)val) << 1);
                       } break;

               case 25 :     /* Connection */
                       {
                               InstrBuf[edpatch][10] = InstrBuf[edpatch][10] & 0xFE;
                               InstrBuf[edpatch][10] = InstrBuf[edpatch][10] | val;
                       } break;
       }
}