/* fmsynt plays the fm chip with MIDI messages from MIDI IN */
/* channel 10 goes directly to MIDI out                     */

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

extern void init_midi(void);
extern void exit_midi(void);
extern int get_midibyte(unsigned char * midiinbyte);
extern void out_midibyte(unsigned char midioutbyte);

const NoteOff1        = 128,
         NoteOff16       = 143,
         NoteOn1         = 144,
         NoteOn16        = 159,
         PolyPress1      = 160,
         PolyPress16     = 175,
         ControlChange1  = 176,
         ControlChange16 = 191,
         ProgramChange1  = 192,
         ProgramChange16 = 207,
         ChanPress1      = 208,
         ChanPress16     = 223,
         PitchWheel1     = 224,
         PitchWheel16    = 239,
         SysEx           = 240,
         EOX             = 247 ;


unsigned char run_status_buf;

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;

int notedur;
unsigned char old_note,velocity,octave;
int note_stopped;
long far *ticks;
long tick_cntr;

main(int argc,char **argv)
{
unsigned char bMidiByte,note,channel,velocity,program_num;

       if(init(argc,argv[1]))
       {
               while(1)
               {
                       if (get_midibyte(&bMidiByte))
                       {

                        /*     out_midibyte(bMidiByte); */

                               if(bMidiByte >= NoteOff1)
                                       run_status_buf = bMidiByte;
                               else
                               {
                                       channel = run_status_buf & 0xF;

                                       if((run_status_buf >= NoteOn1) && (run_status_buf <= NoteOn16))
                                       {
                                               while(! get_midibyte(&velocity));
                                       /*      out_midibyte(velocity); */

                                               if(channel_on[channel])
                                                       fmplay(channel,bMidiByte,velocity);
                                       }
                                       else
                                       {
                                               if((run_status_buf >= ProgramChange1) && (run_status_buf <= ProgramChange16))
                                                       sbfd_program_change(channel,bMidiByte);
                                       }
                               }
                       }

                       if(! note_stopped)
                               if(ticks[0]-tick_cntr > notedur)
                                       stop_note();

                       if(kbhit())
                               if(! process_key()) break;
               }
               sbfd_music_off();
               exit_midi();
               clrscr();
       }
}

int init(int argc,char *patchfile)
{
int i,j;

       if ( ! GetEnvSetting() )
       {
               if (sbc_check_card() & 4)
               {
                       if (sbc_test_int())
                       {
                               init_midi();
                               sbfd_init();

                               ticks = MK_FP(0,0x46c);
                               note_stopped = 1;
                               velocity = 64;
                               notedur = 20;
                               octave = 5;

                               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(argc > 1)
                                       loadpatches2(patchfile);

                               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);
}

draw_screen()
{
int i;
       clrscr();
       printf("*** FM MIDI Synth *** \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]);
}

int process_key()
{
int scan,modifiers;

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

       if(scan == 1) return(0);

       if(modifiers & 8)
       {
               switch(scan) {
                       case 45 : setduration();break;     /* Alt-X */
                       case 47 : setveloc();break;        /* Alt-V */
                       case 25 : loadpatch();break;       /* Alt-P */
                       case 38 : loadpatches();break;     /* Alt-L */
                       case 31 : savepatches();break;     /* Alt-S */

                       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 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;
               }
       }
       return(1);
}

loadpatch()
{
FILE* f;

char patchname[80],patchnumstr[10];

       clrscr();
       printf("Name of single patch to load: ");
       gets(patchname);
       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);
       }
       else
       {
               printf("Couldn't find %s!\n",patchname);
               delay(1000);
       }
       draw_screen();
}


loadpatches()
{
char patchsetname[80];

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

       loadpatches2(patchsetname);

       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();
       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);

       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);
}

setduration()
{
char notedurstr[10];
       printf("\nSet note length (times 55 ms): ");
       gets(notedurstr);
       notedur = atoi(notedurstr);
}

setveloc()
{
char velocstr[10];
       printf("\nSet velocity (0-127): ");
       gets(velocstr);
       velocity = atoi(velocstr);
}


playnote(unsigned char offs)
{
unsigned char note;

       if(! note_stopped)
               stop_note();

       note = (octave*12)+offs;

       out_midibyte(0x90);                          /* Note On channel 1 */
       out_midibyte(note);
       out_midibyte(velocity);

       tick_cntr = ticks[0];
       old_note = note;
       note_stopped = 0;
}

stop_note()
{
       out_midibyte(0x90);                          /* Note Off */
       out_midibyte(old_note);
       out_midibyte(0);
       note_stopped = 1;
}