/* fmsynt plays the fm chip with MIDI messages from MIDI IN */
/* With Built-in FM editor                                  */
/* now with MOUSE support!!!!                               */


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

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

#define SbProID 100
#define KeyUp 0x80

typedef unsigned long ulong;

struct MIDIpacket {
       unsigned char track;
       ulong   msec;
       unsigned char midbyte1;
       unsigned char midbyte2;
       unsigned char midbyte3;
       struct MIDIpacket *next,*before;
}  *point, *sequence_start, *sequence_end;

static struct MIDIpacket *last_pos;

unsigned long ms,old_ms;
unsigned char status,kanel,nn,vl;
unsigned char trk;
int solo;

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, playmode, recmode;


main(int argc,char **argv)
{
       if(init(argc,argv[1]))
       {
               while(1)
               {
                       if(playmode && (point != NULL))
                               process_recorded();
                       if(playmode)
                               process_playkey();
                       else
                         if(kbhit())
                                process_key();
               }
       }
}

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 46 : clr_sequence();break;
                       case 20 : select_track();break;
                       case 45 : set_solo();break;
                /*

                       case 120 : trk = 1; break;
                       case 121 : trk = 2; break;
                       case 122 : trk = 3; break;
                       case 123 : trk = 4; break;
                       case 124 : trk = 5; break;
                       case 125 : trk = 6; break;
                       case 126 : trk = 7; break;
                       case 127 : trk = 8; break;
                       case 128 : trk = 9; break;
                       case 129 : trk = 0; break;
                       case 30 : trk = 10; break;
                       case 48 : trk = 11; break;
                       case 46 : trk = 12; break;
                       case 32 : trk = 13; break;
                       case 18 : trk = 14; break;
                       case 33 : trk = 15; break;

               */

               }
               draw_screen();
       }
       else
       {
               switch(scan){
                       case 25 : rec_play(0); break;
                       case 19 : rec_play(1);break;
                       case 1 :  quit(0);
               }
       }
}


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


process_recorded()
{
       if(miditicks-ms >= point->msec)
       {
               if(solo && (point->track != trk))
               {
                  point = point->next;
                  return;
               }

               status = point->midbyte1;
               kanel = status & 0xF;

               switch(status & 0xF0){
                       case 0x90 :
                       {
                               nn = point->midbyte2;
                               vl = point->midbyte3;

                               if(vl)
                                       sbfd_note_on(kanel,nn,vl);
                               else
                                       sbfd_note_off(kanel,nn,0);

                       } break;
                       case 0x80 :
                       {
                               nn = point->midbyte2;
                               vl = point->midbyte3;
                               sbfd_note_off(kanel,nn,0);
                       } break;
                       case 0xC0 : sbfd_program_change(kanel,point->midbyte2);
               }
               point = point->next;
       }
}


select_track()
{
unsigned char ts[10];

       gotoxy(1,22);
       printf("Select track (1-16): \n");
       gets(ts);
       trk = (unsigned char)(atoi(ts) - 1);
       gotoxy(1,22);
       printf("                           ");
}


set_solo()
{
unsigned char *mode[2] = {"Off","On"};

       solo ^= 1;
       gotoxy(1,22);
       printf("Solo %s\n",mode[solo]);
       delay(1000);
       gotoxy(1,22);
       printf("         ");
}


rec_play(int mode)
{
       if(mode == 1)
               recmode = 1;
       else
               recmode = 0;

       ms = miditicks;
       point = sequence_start;
       goto_playmode();
}



clr_sequence()
{
       point = sequence_start;         /* f�rsta block i gruppen      */
       do {
               farfree(point);                  /* g�r fri nuvarande block        */
               point = point->next;            /* peka till n�sta                */
         } while (point->next != NULL);    /* sluta n�r next �r NULL      */

       sequence_start = sequence_end = NULL;
       last_pos = sequence_start;

       gotoxy(1,22);
       printf("Sequence Cleared!\n");
       delay(1000);
       gotoxy(1,22);
       printf("                    ");
}



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



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(recmode)
               add_packet(trk,miditicks-ms,0x90 | trk,note,velocity);
       sbfd_note_on(trk,note,velocity);
       old_note = note;
}

stop_note()
{
       if(recmode)
               add_packet(trk,miditicks-ms,0x90 | trk,old_note,0);
       sbfd_note_off(trk,old_note,0);
}

change_program(int scan,int button)
{
       switch(scan){
               case 73 : {
                       if(program < 127)
                               program++;
               }break;
               case 81 : {
                       if(program > 0)
                               program--;
               }break;
       }

       if(recmode)
               add_packet(trk,miditicks-ms,0xC0 | trk,program,0);

       sbfd_program_change(trk,program);

       gotoxy(65,1);printf("%-3u",program);
}




add_packet(trk,tstamp,c1,c2,c3)
unsigned long tstamp;
unsigned char c1,c2,c3;
{
       struct MIDIpacket *scratch,*insert_pt,*get_insert_point();

       scratch = (struct MIDIpacket *) farmalloc(16);

       if(scratch == NULL)
       {
                 fprintf(stderr,"Sorry, out of memory!\n");
                 delay(1000);
                 quit(1);
       }

       scratch->track = trk;
       scratch->msec = tstamp;
       scratch->midbyte1 = c1;
       scratch->midbyte2 = c2;
       scratch->midbyte3 = c3;

       /* Where in the linked list should this packet go? */

       if(sequence_start == NULL)       /* the list is empty */
       {
               sequence_start = sequence_end = scratch;
               scratch->before = NULL;
               scratch->next = NULL;
       }
       else
       {
               insert_pt = get_insert_point(sequence_start,sequence_end,tstamp);
               scratch->track = trk;

               if(insert_pt == NULL) /* insert the new bottom record */
               {
                       sequence_end->next = scratch;
                       scratch->before = sequence_end;
                       scratch->next = NULL;
                       sequence_end = scratch;
               }
               else if(insert_pt == sequence_start) /* insert the new top record */
               {
                       sequence_start->before = scratch;
                       scratch->before = NULL;
                       scratch->next = sequence_start;
                       sequence_start = scratch;
               }
               else  /* insert before the insert pointer */
               {
                       scratch->next = insert_pt;
                       scratch->before = insert_pt->before;
                       insert_pt->before = scratch;
                       (scratch->before)->next = scratch;
               }
       }
} /* end of add_packet() */


struct MIDIpacket *get_insert_point(top,bottom,tstamp)
struct MIDIpacket *top,*bottom;
unsigned long tstamp;
{
       if(tstamp > bottom->msec) return(NULL); /* tack onto end of list */
       if(tstamp < top->msec) return(top); /* insert as the new first record */

       if(tstamp >= (last_pos->before)->msec)
       {
               while(tstamp > last_pos->msec)
                       last_pos = last_pos->next;
               return(last_pos);
       }
       else
       {
               last_pos = sequence_start;             /* Apparently a new track */
               while(tstamp > last_pos->msec)
                       last_pos = last_pos->next;
               return(last_pos);
       }
}






/* ==================  Initialization and other routines =============== */





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

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

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

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


                               sequence_start = sequence_end = NULL;
                               last_pos = sequence_start;

                               trk = 0;
                               solo = 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("fmsynt.cfg","rb"))
                                       get_config_settings(f);

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


quit(mode)
{
int k, butstat;

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

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

       }else
               k = 28;

       if(k == 28 || k == 21)
       {
               point = sequence_start;         /* f�rsta block i gruppen      */
               do {
                       farfree(point);             /* g�r fri nuvarande block        */
                       point = point->next;        /* peka till n�sta                */
               } while (point->next !=NULL);    /* sluta n�r next �r NULL      */

               sbfd_music_off();
               exit_midi();
               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("fmsynt.cfg","wb");
       fwrite(&channel_on[0],1,16,cfgf);
       fwrite(&velocity,1,1,cfgf);
       fclose(cfgf);
}

draw_screen()
{
int i;
       clrscr();
       printf("*** THE Sequencer! ***                          Current Program: %-3u\n\n",program);
       printf("^V Set Velocity  ^O All notes off  ^Y FM Editor  ^Z List Files  ^P Load Patch\n\n");
       printf("^L Load 128-patch set  ^S Save 128-patch set  ^W Save Settings  ^C Clear seq\n\n");
       printf("^T Select Track  ^X Solo  (R)ecord  (P)lay  (Q)uit\n\n\n");

       printf("Current Track: %d\n",trk+1);
}

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

       done = findfirst("*.*",&ffblk,0);
       while (!done)
       {
               printf("%16s", ffblk.ff_name);
               done = findnext(&ffblk);
       }
       printf("\n\nPress any key...");

       getch();

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




#include "fmedit.c"