#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <process.h>
#include <i86.h>
#include "sfxmain.h"
#include "sfxinfo.h"

#define nodebug

//
//  G l o b a l    V a r i a b l e s  . . .
//

unsigned short int DOUBLE_MEM = 0;

#define TRUE  = 1;
#define FALSE = 0;

unsigned short int Songpos;
char  BPM,Speed;
char  Row,Pattern;
char  p_note;
char  p_inst;
char  p_vol;
char  p_eff;
char  p_para;

char  *pos_patt;
char  maxrows,n;

char row_changed;
extern unsigned int GusDramStart;

char *modname;
char sbuffer[80];

///  X M  -  P L A Y E R 

typedef unsigned short int word;
typedef unsigned int dword;
typedef unsigned char uchar;
typedef signed char schar;


struct xmheader  {
  char                xm_id[17];
  char                module_name[20];
  char                One_a;
  char                tracker_name[20];
  uchar               version_number_lo; 
  uchar               version_number_hi; 
  dword               header_size;
  word                song_length;
  word                restart_position;
  word                num_channels;
  word                num_patterns;
  word                num_instruments;
  word                flags;
  word                default_tempo;
  word                default_bpm;
  char                pat_table[256];
};

struct patterns {
  dword     pattern_header_length;
  char      packing_type;
  word      number_of_rows;
  word      packed_size;
  char     *packed_data; 
};

struct inst_header {
  dword  inst_hsize;
  char  samp_numbers[96];
  unsigned short int  vol_envelope[24];
  unsigned short int  pan_envelope[24];
  char  num_vol_points;
  char  num_pan_points;
  char  vol_sustain_p;
  char  vol_loop_startp;
  char  vol_loop_endp;
  char  pan_sustain_p;
  char  pan_loop_startp;
  char  pan_loop_endp;
  char  vol_type;
  char  pan_type;
  char  vib_type;
  char  vib_sweep;
  char  vib_depth;
  char  vib_rate;
  word   vol_fadeout;
  word   reserved;
};

word sheadersize = 4+4+4+1+1+1+1+1+1+1+22+4+4+1+1;

struct sample_header {
  dword  sample_length;
  dword  sample_loop_start;
  dword  sample_loop_length;
  char  volume;
  signed char  finetune;
  char  type;
  char  panning;
  char  relative_note;
  char   reserved;
  char   sample_name[22];
  char far *s_data;

  dword Gus_Start;
  char Is_16Bit;
  char Loop_Mode;
};

struct instruments {
  dword                 inst_size;
  char                  inst_name[22];
  char                  inst_type;
  word                  num_samples;
  struct inst_header   *iheader;
  struct sample_header *sheader[16];
};

struct Channel_Type {
  char    note;
  int     period;
  word    freq;
  short int volume;
  char    effect;
  char    old_effect;
  char    param;
  char    veffect;
  char    inst_no;
  unsigned short int smpno; /* Number of sample in Instrument */
  short int porta;
  char last_note;
  char dest_note;
  char Start_note;
  short int finetune;
  short int last_param;

  short int vol_val;
  short int vol_pos;
  short int vol_pt1;
  short int vol_pt2;

  short int pan_val;
  short int pan_pos;
  short int pan_pt1;
  short int pan_pt2;

  char is_vol_env;
  char is_vol_sus;
  char is_vol_loop;
  char is_pan_env;
  char is_pan_sus;
  char is_pan_loop;
  char Wait_Keyoff;

  int last_period;
  int dest_period;

  int vibpos;
  int vib_depth;
  int vib_rate;

  int Startadd;
  char ret_count;
};

 extern short int Actual_Tick;
 char *screen = 0x0B8000;
 char *sampdata[128][16];
 struct instruments   insts[128];
 struct patterns      patts[256];
 FILE                 *file;
 char                 dummyb;
 struct xmheader      xmh;
 word                 i,j,k;
 struct Channel_Type  Channel[32];

 char single_note;

 unsigned int Mastervolume = 240;
unsigned char Break_The_Pattern;
char replay_note;
char replay_freq;



unsigned short int mytab[12]={
	1712*16,1616*16,1524*16,1440*16,1356*16,1280*16,
	1208*16,1140*16,1076*16,1016*16,960*16,907*16
};

static short int VibratoTable[64]={
	0,24,49,74,97,120,141,161,
	180,197,212,224,235,244,250,253,
	255,253,250,244,235,224,212,197,
  180,161,141,120,97,74,49,24,
  0,-24,-49,-74,-97,-120,-141,-161,
  -180,-197,-212,-224,-235,-244,-250,-253,
  -255,-253,-250,-244,-235,-224,-212,-197,
  -180,-161,-141,-120,-97,-74,-49,-24
};


// linear periods to frequency translation table:

unsigned short int lintab[768]={
16726,16741,16756,16771,16786,16801,16816,16832,16847,16862,16877,16892,16908,16923,16938,16953,
16969,16984,16999,17015,17030,17046,17061,17076,17092,17107,17123,17138,17154,17169,17185,17200,
17216,17231,17247,17262,17278,17293,17309,17325,17340,17356,17372,17387,17403,17419,17435,17450,
17466,17482,17498,17513,17529,17545,17561,17577,17593,17608,17624,17640,17656,17672,17688,17704,
17720,17736,17752,17768,17784,17800,17816,17832,17848,17865,17881,17897,17913,17929,17945,17962,
17978,17994,18010,18027,18043,18059,18075,18092,18108,18124,18141,18157,18174,18190,18206,18223,
18239,18256,18272,18289,18305,18322,18338,18355,18372,18388,18405,18421,18438,18455,18471,18488,
18505,18521,18538,18555,18572,18588,18605,18622,18639,18656,18672,18689,18706,18723,18740,18757,
18774,18791,18808,18825,18842,18859,18876,18893,18910,18927,18944,18961,18978,18995,19013,19030,
19047,19064,19081,19099,19116,19133,19150,19168,19185,19202,19220,19237,19254,19272,19289,19306,
19324,19341,19359,19376,19394,19411,19429,19446,19464,19482,19499,19517,19534,19552,19570,19587,
19605,19623,19640,19658,19676,19694,19711,19729,19747,19765,19783,19801,19819,19836,19854,19872,
19890,19908,19926,19944,19962,19980,19998,20016,20034,20052,20071,20089,20107,20125,20143,20161,
20179,20198,20216,20234,20252,20271,20289,20307,20326,20344,20362,20381,20399,20418,20436,20455,
20473,20492,20510,20529,20547,20566,20584,20603,20621,20640,20659,20677,20696,20715,20733,20752,
20771,20790,20808,20827,20846,20865,20884,20902,20921,20940,20959,20978,20997,21016,21035,21054,
21073,21092,21111,21130,21149,21168,21187,21206,21226,21245,21264,21283,21302,21322,21341,21360,
21379,21399,21418,21437,21457,21476,21496,21515,21534,21554,21573,21593,21612,21632,21651,21671,
21690,21710,21730,21749,21769,21789,21808,21828,21848,21867,21887,21907,21927,21946,21966,21986,
22006,22026,22046,22066,22086,22105,22125,22145,22165,22185,22205,22226,22246,22266,22286,22306,
22326,22346,22366,22387,22407,22427,22447,22468,22488,22508,22528,22549,22569,22590,22610,22630,
22651,22671,22692,22712,22733,22753,22774,22794,22815,22836,22856,22877,22897,22918,22939,22960,
22980,23001,23022,23043,23063,23084,23105,23126,23147,23168,23189,23210,23230,23251,23272,23293,
23315,23336,23357,23378,23399,23420,23441,23462,23483,23505,23526,23547,23568,23590,23611,23632,
23654,23675,23696,23718,23739,23761,23782,23804,23825,23847,23868,23890,23911,23933,23954,23976,
23998,24019,24041,24063,24084,24106,24128,24150,24172,24193,24215,24237,24259,24281,24303,24325,
24347,24369,24391,24413,24435,24457,24479,24501,24523,24545,24567,24590,24612,24634,24656,24679,
24701,24723,24746,24768,24790,24813,24835,24857,24880,24902,24925,24947,24970,24992,25015,25038,
25060,25083,25105,25128,25151,25174,25196,25219,25242,25265,25287,25310,25333,25356,25379,25402,
25425,25448,25471,25494,25517,25540,25563,25586,25609,25632,25655,25678,25702,25725,25748,25771,
25795,25818,25841,25864,25888,25911,25935,25958,25981,26005,26028,26052,26075,26099,26123,26146,
26170,26193,26217,26241,26264,26288,26312,26336,26359,26383,26407,26431,26455,26479,26502,26526,
26550,26574,26598,26622,26646,26670,26695,26719,26743,26767,26791,26815,26839,26864,26888,26912,
26937,26961,26985,27010,27034,27058,27083,27107,27132,27156,27181,27205,27230,27254,27279,27304,
27328,27353,27378,27402,27427,27452,27477,27502,27526,27551,27576,27601,27626,27651,27676,27701,
27726,27751,27776,27801,27826,27851,27876,27902,27927,27952,27977,28003,28028,28053,28078,28104,
28129,28155,28180,28205,28231,28256,28282,28307,28333,28359,28384,28410,28435,28461,28487,28513,
28538,28564,28590,28616,28642,28667,28693,28719,28745,28771,28797,28823,28849,28875,28901,28927,
28953,28980,29006,29032,29058,29084,29111,29137,29163,29190,29216,29242,29269,29295,29322,29348,
29375,29401,29428,29454,29481,29507,29534,29561,29587,29614,29641,29668,29694,29721,29748,29775,
29802,29829,29856,29883,29910,29937,29964,29991,30018,30045,30072,30099,30126,30154,30181,30208,
30235,30263,30290,30317,30345,30372,30400,30427,30454,30482,30509,30537,30565,30592,30620,30647,
30675,30703,30731,30758,30786,30814,30842,30870,30897,30925,30953,30981,31009,31037,31065,31093,
31121,31149,31178,31206,31234,31262,31290,31319,31347,31375,31403,31432,31460,31489,31517,31546,
31574,31602,31631,31660,31688,31717,31745,31774,31803,31832,31860,31889,31918,31947,31975,32004,
32033,32062,32091,32120,32149,32178,32207,32236,32265,32295,32324,32353,32382,32411,32441,32470,
32499,32529,32558,32587,32617,32646,32676,32705,32735,32764,32794,32823,32853,32883,32912,32942,
32972,33002,33031,33061,33091,33121,33151,33181,33211,33241,33271,33301,33331,33361,33391,33421
};


//
//  P r o t o t y p e s  . . .
//

void load_module(char *modulename);
void process_patternrow();
void NewPattern(char patno);
void screen_update();
//unsigned int get_freq(unsigned short int note);
void gotoxy(char x, char y);
extern void far cls(void);



unsigned int Interpolate(unsigned int p,unsigned int p1,unsigned int p2,unsigned int v1,unsigned int v2)
{
  unsigned int dp,dv,di;

	if(p1==p2) return v1;

	dv=v2-v1;
	dp=p2-p1;
	di=p-p1;

  return v1 + ((int)(di*dv) / dp);
}


unsigned int getlinearperiod(char note,unsigned int fine)
{
  return((10L*12*16*4)-((unsigned int)(note-2)*16*4)-(fine/2)+64);
//  return((10L*12*16*4)-((unsigned int)(note)*16*4)-(fine/2)+64);
//  return((10L*12*16*4)-((unsigned int)note*16*4)-(fine/2)+128);
}



unsigned int Getperiod(char note,unsigned int c2spd)
{
  return(getlinearperiod(note,c2spd));
}


int GetFreq2(int period)
{
  int okt;
	long frequency;
  period=7680-period;
  okt=period/768;
  frequency=lintab[period%768];
	frequency<<=2;
  return(frequency>>(7-okt));
}


unsigned int get_freq(unsigned short int channel1)
{
  unsigned short int note;
  int RealNote,period,FineTune,Frequency;

  if (Channel[channel1].note == 0) {
    RealNote = Channel[channel1].last_note + insts[Channel[channel1].inst_no].sheader[Channel[channel1].smpno]->relative_note;
  }
  else {
    RealNote = Channel[channel1].note + insts[Channel[channel1].inst_no].sheader[Channel[channel1].smpno]->relative_note;
  } 

  FineTune = Channel[channel1].finetune;


  period = getlinearperiod(RealNote,FineTune+128);
  Frequency = GetFreq2(period);
  return(period);
  return(round_w(Frequency));
}


void Init_Instruments()
{
 struct inst_header  dumyi; 

  for (i = 0; i < 256; i++) {
    patts[i].pattern_header_length = 0;
    patts[i].packing_type = 0;
    patts[i].number_of_rows = 1;
    patts[i].packed_size = 0;
    if(( patts[i].packed_data = malloc(patts[i].packed_size+64)) == NULL) {
      sfxerror(2);
    }
  }

  for (i = 0; i < 128; i++) {

  insts[i].inst_size = 0;
  strcpy(insts[i].inst_name,"empty");
  insts[i].inst_type = 0;
  insts[i].num_samples = 0;

  if ((insts[i].iheader = malloc(sizeof(dumyi)+64)) == NULL) {
     sfxerror(3);
  }

    insts[i].iheader->inst_hsize = 0;
    memset(insts[i].iheader->samp_numbers,0,96);
    memset(insts[i].iheader->vol_envelope,0,24);
    memset(insts[i].iheader->pan_envelope,0,24);
    insts[i].iheader->num_vol_points = 0;
    insts[i].iheader->num_pan_points = 0;
    insts[i].iheader->vol_sustain_p = 0;
    insts[i].iheader->vol_loop_startp = 0;
    insts[i].iheader->vol_loop_endp = 0;
    insts[i].iheader->pan_sustain_p = 0;
    insts[i].iheader->pan_loop_startp = 0;
    insts[i].iheader->pan_loop_endp = 0;
    insts[i].iheader->vol_type = 0;
    insts[i].iheader->pan_type = 0;
    insts[i].iheader->vib_type = 0;
    insts[i].iheader->vib_sweep = 0;
    insts[i].iheader->vib_depth = 0;
    insts[i].iheader->vib_rate = 0;
    insts[i].iheader->vol_fadeout = 0;
  }
}


void Init_Channels()
{
  for (i = 0; i < 32; i++) {
    Channel[i].note = 0;
    Channel[i].period = 0;
    Channel[i].freq = 0;
    Channel[i].volume = 0;
    Channel[i].effect = 0;
    Channel[i].old_effect = 0;
    Channel[i].param = 0;
    Channel[i].veffect = 0;
    Channel[i].inst_no = 0;
    Channel[i].smpno = 0; 
    Channel[i].porta = 0;
    Channel[i].last_note = 0;
    Channel[i].dest_note = 0;
    Channel[i].Start_note = 0;
    Channel[i].finetune = 0;
    Channel[i].last_param = 0;

    Channel[i].vol_val = 0;
    Channel[i].vol_pos = 0;
    Channel[i].vol_pt1 = 0;
    Channel[i].vol_pt2 = 0;

    Channel[i].pan_val = 0;
    Channel[i].pan_pos = 0;
    Channel[i].pan_pt1 = 0;
    Channel[i].pan_pt2 = 0;

    Channel[i].is_vol_env = 0;
    Channel[i].is_vol_sus = 0;
    Channel[i].is_vol_loop = 0;
    Channel[i].is_pan_env = 0;
    Channel[i].is_pan_sus = 0;
    Channel[i].is_pan_loop = 0;
    Channel[i].Wait_Keyoff = 0;

    Channel[i].last_period = 0;
    Channel[i].dest_period = 0;

    Channel[i].vibpos = 0;
    Channel[i].vib_depth = 0;
    Channel[i].vib_rate = 0;

    Channel[i].Startadd = 0;
    Channel[i].ret_count = 0;
  }
}

void myprint(char *s,short int xpos,short int ypos,short int length)
{
 char *scrnp;
 short int k;
 char buffer[80];

 memset(buffer,32,80);
 strcpy(buffer,s);
 scrnp = 0x0B8000 + ypos*160 + xpos*2;

 k=0;
 while ((k < length) && (buffer[k] != 0)) {
   memcpy(scrnp,&buffer[k],1);
   k ++;
   scrnp += 2;
 }
}


void show_mainscreen()
{
  char buffer[80];

  memcpy(screen,&SFXMAIN,80*25*2);
  myprint("   ",21,4,3);
  myprint("   ",27,4,3);
  myprint("   ",51,4,3);
  myprint("   ",57,4,3);
  myprint("                                        ",22,3,35);
  myprint(xmh.module_name,24,3,20);
}


void show_info()
{
  memcpy(screen,&SFXINFO,80*25*2);
  getch();
  show_mainscreen();
}



//
//  M a i n  p r o g r a m  . . .
//


void main(int argc, char *argv[])
{
 char chchar = '„';

 if (argc < 2) {
   sfxerror(0);
 }

 Init_Gus(0x0220);
 speaker_off();
 Break_The_Pattern = 0;
 Init_Instruments();
 Init_Channels();

 memcpy(screen,&SFXMAIN,80*25*2);
 load_module(argv[1]);
 row_changed = 0;

 Set_Timer_Defaults();
 SetTimer(xmh.default_bpm-1,xmh.default_tempo-1);

 for (i = 0; i < 32; i++) {
   UltraSetPanning(i,32);
 }

 init_timer();
 speaker_on();

 cls();
 show_mainscreen();


 while (chchar != 27) {
    if (kbhit()) {
      chchar = getch();
//        process_patternrow();
      switch(chchar) {
       case '+' : if (Mastervolume < 245) {
                    Mastervolume += 10;
                    chchar = '„';
                  }
                  break;  
       case '-' : if (Mastervolume > 10) {
                    Mastervolume -= 10;
                    chchar = '„';
                  }
                  break;
       case 'i' : show_info();
                  break;
       case 'o' : spawnl(P_WAIT,"command.com",NULL);
                  break;
       case 'v' : Songpos ++;
                  Break_The_Pattern = 1;
                  chchar = '„';
                  break;

       case 'z' : Songpos -= 2;
                  Break_The_Pattern = 1;
                  chchar = '„';
                  break;

      };
    }
   if (row_changed == 1) {
     row_changed = 0;
     screen_update();
   }
 }
 reset_timer();
 stop_gus();
}

void gotoxy(char x, char y)
{
 union REGS my_regs;
 my_regs.h.ah = 2;
 my_regs.h.bh = 0;
 my_regs.h.dl = x;
 my_regs.h.dh = y;
 int386(0x10, &my_regs, &my_regs);
}

void sfxerror(int errornumber)
{
  switch(errornumber) {
    case 0:
      printf("\n\n");
      printf("[þSFXPROþ] ERROR 000 : Wrong Syntax \n\n");
      printf("USAGE: SFXPRO [MODULENAME.XM]\n\n");                 
      exit(1);
      break;
    case 1:
      printf("[þSFXPROþ] ERROR 001 : Module not found \n");
      exit(1);
      break;
    case 2:
      printf("[þSFXPROþ] ERROR 002 : Out of memory loading Pattern-Data ... \n");
      exit(1);
      break;
    case 3:
      printf("[þSFXPROþ] ERROR 003 : Out of memory loading Instrument Data ... \n");
      exit(1);
      break;
    case 4:
      printf("[þSFXPROþ] ERROR 004 : Out of memory loading Sample Data ... \n");
      exit(1);
      break;
  }
}


void screen_update()
{
 short int m;
 char *scrnp;
 char buffer[80];

 memset(buffer,0,80);
 sprintf(buffer,"%d",Songpos);
 myprint("   ",21,4,3);
 myprint(buffer,21,4,3);

 memset(buffer,0,80);
 sprintf(buffer,"%d",Row);
 myprint("   ",27,4,3);
 myprint(buffer,27,4,3);
 
 memset(buffer,0,80);
 sprintf(buffer,"%d",BPM+1);
 myprint("   ",51,4,3);
 myprint(buffer,51,4,3);

 memset(buffer,0,80);
 myprint("   ",57,4,3);
 sprintf(buffer,"%d",Speed+1);
 myprint(buffer,57,4,3);

  for (n = 0; n < xmh.num_channels; n += 2) {
    for (m = 10; m <= 39; m++) {
      scrnp = 0x0B8000 + n*80+800 + m*2;
      memcpy(scrnp,"ú",1);
    }
    for (m = 0; m < Channel[n].volume*28/64; m++) {
      scrnp = 0x0B8000 + n*80+800 + 78 - m*2;
      memcpy(scrnp,"þ",1);
    }
  }        

  for (n = 0; n < xmh.num_channels-1; n += 2) {
    for (m = 42; m <= 70; m++) {
      scrnp = 0x0B8000 + n*80 + 800 + m*2;
      memcpy(scrnp,"ú",1);
    }
    for (m = 0; m < Channel[n+1].volume*28/64; m++) {
      scrnp = 0x0B8000 + n*80 + 800 + 84 + m*2;
      memcpy(scrnp,"þ",1);
    }
  }

  for (n = 0; n < xmh.num_channels; n ++) {
    if (Channel[n].volume > 64) {
      gotoxy(10,15+n);
      printf("Problems in Channel %d, Pattern %d Row %d\n",n,Songpos,Row);
      getch();
    }
  }

}


void convert_sample(signed char *s,unsigned int length)
{
 unsigned int n;
 int new,ltmp;
 signed char old,tmp;
 signed char *tmpptr;

 tmpptr = s;

 old = *tmpptr;
 tmpptr++;
 for (n = 1; n <= length; n++) {
   tmp  = *tmpptr;
   ltmp = tmp;
   new  = ltmp + old;
   tmp  = new;

   *tmpptr = tmp;
   old = tmp;

   tmpptr++;
 }
}


void convert_sample16(short int *s,unsigned int length)
{
 unsigned int n;
 int new,ltmp;
 short int old,tmp;
 short int *tmpptr;

 tmpptr = s;

 old = *tmpptr;
 tmpptr++;
 for (n = 1; n <= length >> 1; n ++) {
   tmp  = *tmpptr;
   ltmp = tmp;
   new  = ltmp + old;
   tmp  = new;

   *tmpptr = tmp;
   old = tmp;

   tmpptr ++;
 }
}


void load_module(char *modulename)
{
 int slength;
 char far *tmpptr;
 char buffer[80];

 GusDramStart = 0;

 if((file = fopen(modulename,"rb")) == NULL)
   {
    sfxerror(1);
   }

  fread(&xmh,sizeof(xmh),1,file);
  xmh.xm_id[16] = 0;
  xmh.module_name[19] = 0;
  xmh.tracker_name[19] = 0;

  //  ======================================================================
  //    Read Pattern
  //  ======================================================================
  for (i = 0; i < xmh.num_patterns; i++) {
    fread(&patts[i],9,1,file);
    if (patts[i].packed_size > 0) {
      if(( patts[i].packed_data = malloc(patts[i].packed_size+64)) == NULL)
        {
          sfxerror(2);
        }
      fread(patts[i].packed_data,patts[i].packed_size,1,file);
    }
    memset(buffer,0,80);
    sprintf(buffer,"[þSFXPROþ] Pattern %d loaded ...        ",i);
    myprint(buffer,7,22,40);
  }  

  #ifdef debug
     gotoxy(1,1);
     printf("Defined Instruments : %d\n",xmh.num_instruments);
     getch();
  #endif


  //  ======================================================================
  //    Load Instruments
  //  ======================================================================
  for (i = 0; i < xmh.num_instruments; i++) {
    memset(buffer,0,80);
    sprintf(buffer,"[þSFXPROþ] Loading Instrument  %d ...      ",i);
    #ifdef debug
      getch();
    #endif
    myprint(buffer,7,22,40);

    fread(&insts[i],29,1,file);
    #ifdef debug
       gotoxy(1,2);
       printf("Number of samples in Instrument %d: %d\n",i,insts[i].num_samples);
       getch();
    #endif
    if (insts[i].num_samples == 0) {
      memset(buffer,0,80);
      sprintf(buffer,"[þSFXPROþ] no instrument data          ");
      myprint(buffer,7,22,40);
      if (insts[i].inst_size > 0) {
        if ((insts[i].iheader = malloc(insts[i].inst_size-29+64)) == NULL) {
            sfxerror(3);
          }
        fread(insts[i].iheader,1,insts[i].inst_size-29,file);
      }
    }
    else {
      //  ======================================================================
      //    Instruments - Allocate and read Header
      //  ======================================================================
      if ((insts[i].iheader = malloc(insts[i].inst_size-29+64)) == NULL) {
          sfxerror(3);
        }
      fread(insts[i].iheader,1,insts[i].inst_size-29,file);

      for (j = 0; j < insts[i].num_samples; j++) {
        //  ======================================================================
        //    Sample - Allocate & read Header of instrument
        //  ======================================================================
        if (insts[i].iheader->inst_hsize == 0) {
          memset(buffer,0,80);
          sprintf(buffer,"[þSFXPROþ] no sample - data                 ");
          myprint(buffer,7,22,40);
        }
        else
        {
          if ((insts[i].sheader[j] = malloc(sheadersize+64)) == NULL) {
            sfxerror(3);
          }
          fread(insts[i].sheader[j],insts[i].iheader->inst_hsize,1,file);

        } // End read sample header
      } // End For loop

      for (j = 0; j < insts[i].num_samples; j++) {
        //  ======================================================================
        //    Allocate & read sample - data of instrument
        //  ======================================================================
        if (insts[i].sheader[j]->sample_length == 0) {
          memset(buffer,0,80);
          sprintf(buffer,"[þSFXPROþ] sample-data of 0 Byte length... ");
          myprint(buffer,7,22,40);
        }
        else {
          slength = insts[i].sheader[j]->sample_length + 64;
          if ((sampdata[i][j] = malloc(slength)) == NULL){
             sfxerror(4);
          };

          fread(sampdata[i][j],insts[i].sheader[j]->sample_length,1,file);

          insts[i].sheader[j]->Loop_Mode = 0;

          // Loop - get type
          switch (insts[i].sheader[j]->type & 3) {
            case 0: break;
            case 1: insts[i].sheader[j]->Loop_Mode += 8;
                    break;
            case 2: insts[i].sheader[j]->Loop_Mode += 8;
                    insts[i].sheader[j]->Loop_Mode += 16;
                    break;
          }

          if ((insts[i].sheader[j]->type & 16) == 16) {
            insts[i].sheader[j]->Loop_Mode += 4;
            convert_sample16(sampdata[i][j],insts[i].sheader[j]->sample_length);
            insts[i].sheader[j]->Is_16Bit = 1;
          }
          else {
            convert_sample(sampdata[i][j],insts[i].sheader[j]->sample_length);
            insts[i].sheader[j]->Is_16Bit = 0;
          }

          insts[i].sheader[j]->Gus_Start = GusDramStart;


/*          if (DOUBLE_MEM == 1) {
          }
          else {*/
            if (insts[i].sheader[j]->Loop_Mode > 0) {  // looped
              mem_2_gus(i,j,insts[i].sheader[j]->sample_length,insts[i].sheader[j]->sample_loop_start);
            }
            else {
              mem_2_gus(i,j,insts[i].sheader[j]->sample_length,0);
            }
//          }


        } // End Data read
      } // end for

/*      // DOUBLE MEM FEATURE
      if (DOUBLE_MEM == 1) {
        for (j = 0; j < insts[i].num_samples; j++) {
          insts[i].sheader[j]->sample_length = insts[i].sheader[j]->sample_length / 2;
          insts[i].sheader[j]->sample_loop_start = insts[i].sheader[j]->sample_loop_start / 2;
          insts[i].sheader[j]->sample_loop_length = insts[i].sheader[j]->sample_loop_length / 2;
        }
      }  */
    }  // end Instrument load
  }  

  Songpos = 0;
  NewPattern(xmh.pat_table[Songpos]);
}


void NewPattern(char patno)
{
 pos_patt = patts[patno].packed_data;
 maxrows  = patts[patno].number_of_rows;
 Row = 0;
}



void Process_Pan_Env()
{
 int pxa;
 int pxb;
 int pva;
 int pvb;
 int dtx;
 int dtv;
 int dtist;
 short int li;

 for (li = 0; li < xmh.num_channels; li++) {
  if (Channel[li].is_pan_env == 1) {
   if ((Channel[li].pan_pos < 324) && (insts[Channel[li].inst_no].iheader->num_pan_points > 1)) {
     Channel[li].pan_pos++;
     // Volume Point reached ?
     if (Channel[li].pan_pos == insts[Channel[li].inst_no].iheader->pan_envelope[Channel[li].pan_pt2*2]) {
       Channel[li].pan_pt1++;
       if (Channel[li].is_pan_sus == 1) {   // Wait for Keyoff ?
         if (Channel[li].pan_pt2 < insts[Channel[li].inst_no].iheader->pan_sustain_p) {
           if (Channel[li].is_pan_loop == 1) {  // If at Loop-Point then loop
             if (Channel[li].pan_pt2 < insts[Channel[li].inst_no].iheader->pan_loop_endp) {
               Channel[li].pan_pt2++;
             }
             else {
               Channel[li].pan_pt1 = insts[Channel[li].inst_no].iheader->pan_loop_startp;
               Channel[li].pan_pt2 = Channel[li].pan_pt1 + 1;
               Channel[li].pan_pos = insts[Channel[li].inst_no].iheader->pan_envelope[Channel[li].pan_pt1*2];
             }
           } // end looping
           else  // no looping
           {
             if (Channel[li].pan_pt2 < insts[Channel[li].inst_no].iheader->num_pan_points) {
               Channel[li].pan_pt2++;
             }
           } // end noo loop
         } // end sustain-p reached
       } // end sustain
       else
       {
         if (Channel[li].is_pan_loop == 1) {  // If at Loop-Point then loop
           if (Channel[li].pan_pt2 < insts[Channel[li].inst_no].iheader->pan_loop_endp) {
             Channel[li].pan_pt2++;
           }
           else {
             Channel[li].pan_pt1 = insts[Channel[li].inst_no].iheader->pan_loop_startp;
             Channel[li].pan_pt2 = Channel[li].pan_pt1 + 1;
             Channel[li].pan_pos = insts[Channel[li].inst_no].iheader->pan_envelope[Channel[li].pan_pt1*2];
           }
         } // end looping
         else {  // no looping
           if (Channel[li].pan_pt2 < insts[Channel[li].inst_no].iheader->num_pan_points) {
             Channel[li].pan_pt2++;
           }
         }
       } // end no sustain
     }
     pva = insts[Channel[li].inst_no].iheader->pan_envelope[Channel[li].pan_pt1*2+1];
     pvb = insts[Channel[li].inst_no].iheader->pan_envelope[Channel[li].pan_pt2*2+1];
     if (pva == pvb) {
       Channel[li].pan_val = pva;
       UltraSetPanning(li,Channel[li].pan_val);
     }
     else {
       pxa = insts[Channel[li].inst_no].iheader->pan_envelope[Channel[li].pan_pt1*2];
       pxb = insts[Channel[li].inst_no].iheader->pan_envelope[Channel[li].pan_pt2*2];
       dtx = pxb - pxa;
       dtv = pvb - pva;
       dtist = Channel[li].pan_pos - pxa; 
       if (dtx > 0) {  
         Channel[li].pan_val = pva + ((dtist * dtv) / dtx);
         UltraSetPanning(li,Channel[li].pan_val);
       }
     }
   } // end new point
  }
 } // end channel loop
}


void Process_Vol_Env()
{
 int pxa;
 int pxb;
 int pva;
 int pvb;
 int dtx;
 int dtv;
 int dtist;
 short int li;

 for (li = 0; li < xmh.num_channels; li++) {
  if ((Channel[li].is_vol_env == 1) && (Channel[li].Wait_Keyoff != 1)) {
   if ((Channel[li].vol_pos < 324) && (insts[Channel[li].inst_no].iheader->num_vol_points > 1)) {
     Channel[li].vol_pos++;
     // Volume Point reached ?
     if (Channel[li].vol_pos == insts[Channel[li].inst_no].iheader->vol_envelope[Channel[li].vol_pt2*2]) {
       Channel[li].vol_pt1++;
       if (Channel[li].is_vol_sus == 1) {   // Wait for Keyoff ?
         if (Channel[li].vol_pt2 < insts[Channel[li].inst_no].iheader->vol_sustain_p) {
           if (Channel[li].is_vol_loop == 1) {  // If at Loop-Point then loop
             if (Channel[li].vol_pt2 < insts[Channel[li].inst_no].iheader->vol_loop_endp) {
               Channel[li].vol_pt2++;
             }
             else {
               Channel[li].vol_pt1 = insts[Channel[li].inst_no].iheader->vol_loop_startp;
               Channel[li].vol_pt2 = Channel[li].vol_pt1 + 1;
               Channel[li].vol_pos = insts[Channel[li].inst_no].iheader->vol_envelope[Channel[li].vol_pt1*2];
             }
           } // end looping
           else  // no looping
           {
             if (Channel[li].vol_pt2 < insts[Channel[li].inst_no].iheader->num_vol_points) {
               Channel[li].vol_pt2++;
             }
           } // end noo loop
         } // end sustain-p reached
         else {
           Channel[li].Wait_Keyoff = 1;
           if (Channel[li].is_vol_loop == 1) {  // If at Loop-Point then loop
             if (Channel[li].vol_pt2 < insts[Channel[li].inst_no].iheader->vol_loop_endp) {
               Channel[li].vol_pt2++;
             }
             else {
               Channel[li].vol_pt1 = insts[Channel[li].inst_no].iheader->vol_loop_startp;
               Channel[li].vol_pt2 = Channel[li].vol_pt1 + 1;
               Channel[li].vol_pos = insts[Channel[li].inst_no].iheader->vol_envelope[Channel[li].vol_pt1*2];
             }
           } // end looping
           else  // no looping
           {
             if (Channel[li].vol_pt2 < insts[Channel[li].inst_no].iheader->num_vol_points) {
               Channel[li].vol_pt2++;
             }
           } // end noo loop
         }
       } // end sustain
       else
       {
         if (Channel[li].is_vol_loop == 1) {  // If at Loop-Point then loop
           if (Channel[li].vol_pt2 < insts[Channel[li].inst_no].iheader->vol_loop_endp) {
             Channel[li].vol_pt2++;
           }
           else {
             Channel[li].vol_pt1 = insts[Channel[li].inst_no].iheader->vol_loop_startp;
             Channel[li].vol_pt2 = Channel[li].vol_pt1 + 1;
             Channel[li].vol_pos = insts[Channel[li].inst_no].iheader->vol_envelope[Channel[li].vol_pt1*2];
           }
         } // end looping
         else {  // no looping
           if (Channel[li].vol_pt2 < insts[Channel[li].inst_no].iheader->num_vol_points) {
             Channel[li].vol_pt2++;
           }
         }
       } // end no sustain
     }
     pva = insts[Channel[li].inst_no].iheader->vol_envelope[Channel[li].vol_pt1*2+1];
     pvb = insts[Channel[li].inst_no].iheader->vol_envelope[Channel[li].vol_pt2*2+1];
     if (pva == pvb) {
       Channel[li].vol_val = pva;
       UltraSetDirectVolume(li,Channel[li].volume * Channel[li].vol_val / 64);
     }
     else {
       pxa = insts[Channel[li].inst_no].iheader->vol_envelope[Channel[li].vol_pt1*2];
       pxb = insts[Channel[li].inst_no].iheader->vol_envelope[Channel[li].vol_pt2*2];
       dtx = pxb - pxa;
       dtv = pvb - pva;
       dtist = Channel[li].vol_pos - pxa; 
       if (dtx > 0) {  
         Channel[li].vol_val = pva + ((dtist * dtv) / dtx);
         UltraSetDirectVolume(li,Channel[li].volume * Channel[li].vol_val / 64);
       }
     }
   } // end new point
  }
 } // end channel loop
}



void process_volume_effects(unsigned short int i)
// i = Channel - Number
{
  char param;

  // Set volume
  if ((Channel[i].veffect >= 0x10) && (Channel[i].veffect <= 0x50)) {
    Channel[i].volume = Channel[i].veffect-0x10;
    UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
  }

  // Volume slide down
  if ((Channel[i].veffect >= 0x60) && (Channel[i].veffect <= 0x6f)) {
    param = Channel[i].veffect - 0x60;
    Channel[i].volume -= param << 2;
    if (Channel[i].volume < 0) {
      Channel[i].volume = 0;
    }
    UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
    Channel[i].veffect = 0;
  }

  // Volume slide up  
  if ((Channel[i].veffect >= 0x70) && (Channel[i].veffect <= 0x7f)) {
    param = Channel[i].veffect - 0x70;
    Channel[i].volume += param << 2;
    if (Channel[i].volume > 64) {
      Channel[i].volume = 64;
    }
    UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
    Channel[i].veffect = 0;
  }

  // FINE Volume slide down
  if ((Channel[i].veffect >= 0x80) && (Channel[i].veffect <= 0x8f)) {
    param = Channel[i].veffect - 0x80;
    Channel[i].volume -= param;
    if (Channel[i].volume < 0) {
      Channel[i].volume = 0;
    }
    UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
    Channel[i].veffect = 0;
  }

  // FINE Volume slide up  
  if ((Channel[i].veffect >= 0x90) && (Channel[i].veffect <= 0x9f)) {
    param = Channel[i].veffect - 0x90;
    Channel[i].volume += param;
    if (Channel[i].volume > 64) {
      Channel[i].volume = 64;
    }
    UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
    Channel[i].veffect = 0;
  }


  // Set vibrato speed
  if ((Channel[i].veffect >= 0xa0) && (Channel[i].veffect <= 0xaf)) {
  }

  // vibrato            
  if ((Channel[i].veffect >= 0xb0) && (Channel[i].veffect <= 0xbf)) {
  }

  // panning            
  if ((Channel[i].veffect >= 0xc0) && (Channel[i].veffect <= 0xcf)) {
    param = Channel[i].veffect - 0xc0;
    Channel[i].pan_val = param << 2;
    UltraSetPanning(i,Channel[i].pan_val);
    Channel[i].veffect = 0;
  }

  // panning slide left
  if ((Channel[i].veffect >= 0xd0) && (Channel[i].veffect <= 0xdf)) {
    param = Channel[i].veffect - 0xd0;
    if ((Channel[i].pan_val - param) > 0) {
      Channel[i].pan_val -= param;
    }
    else {
      Channel[i].pan_val = 0;
    }
    UltraSetPanning(i,Channel[i].pan_val);
    Channel[i].veffect = 0;
  }

  // panning slide right
  if ((Channel[i].veffect >= 0xe0) && (Channel[i].veffect <= 0xef)) {
    param = Channel[i].veffect - 0xe0;
    if ((Channel[i].pan_val + param) < 64) {
      Channel[i].pan_val += param;
    }
    else {
      Channel[i].pan_val = 64;
    }
    UltraSetPanning(i,Channel[i].pan_val);
    Channel[i].veffect = 0;
  }

  // tone portamento  
  if ((Channel[i].veffect >= 0xf0) && (Channel[i].veffect <= 0xff)) {
  }
     
}


void DoVibrato(char i)
{
  char q;
  int tmp;

  q = (Channel[i].vibpos >> 2) & 64;
  tmp=VibratoTable[q];

  tmp = tmp * Channel[i].vib_depth;
  tmp>>=7;
  tmp<<=2;

  Channel[i].period += tmp;
  Channel[i].vibpos += Channel[i].vib_rate;
}

void DoTremolo(char i)
{
  char q;
  int tmp;

  q = (Channel[i].vibpos >> 2) & 64;
  tmp=VibratoTable[q];

  tmp = tmp * Channel[i].vib_depth;
  tmp>>=6;

  Channel[i].volume += tmp;
  if (Channel[i].volume < 0) {
    Channel[i].volume = 0;
  }
  if (Channel[i].volume > 64) {
    Channel[i].volume = 64;
  }
  Channel[i].vibpos += Channel[i].vib_rate;
}



void DoTonePortamento(char i)
{
 int dtp;
 char para;

 if (Channel[i].param != 0) {
   para = Channel[i].param << 2;
   Channel[i].last_param = Channel[i].param;
 }
 else {
   para = Channel[i].last_param << 2;
 }

 dtp = Channel[i].period - Channel[i].dest_period;

 if ((dtp == 0) || (para > abs(dtp))) {
   Channel[i].period = Channel[i].dest_period;
 }
 else {
   if (dtp > 0) {
     Channel[i].period -= para;
   }
   else {
     Channel[i].period += para;
   }
 }
}

void  do_e_effects(char i)
{
 char param;

 unsigned long begin;    /* start location in ultra DRAM */
 unsigned long start;    /* start loop location in ultra DRAM */
 unsigned long end;      /* end location in ultra DRAM */
 unsigned char mode;     /* mode to run the voice (loop etc) */

  

  switch (Channel[i].param >> 4) {
    case 0x00 : // 
                break;
    case 0x01 : /// FINE Portamento UP
                 if ((Channel[i].param & 0x0f) != 0) {
                   Channel[i].period -= (Channel[i].param & 0x0f)<<2;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   Channel[i].period -= (Channel[i].last_param & 0x0f)<<2;
                 }
                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_freq = 0;
                 Channel[i].effect = 255;
                 break;
    case 0x02 : /// FINE Portamento DOWN
                if ((Channel[i].param & 0x0f) != 0) {
                   Channel[i].period += (Channel[i].param & 0x0f)<<2;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   Channel[i].period += (Channel[i].last_param & 0x0f)<<2;
                 }
                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_freq = 0;
                 Channel[i].effect = 255;
                 break;
    case 0x03 : // 
                break;
    case 0x04 : // 
                break;
    case 0x05 : // 
                break;
    case 0x06 : // 
                break;
    case 0x07 : // 
                break;
    case 0x08 : // 
                break;
    case 0x09 : // retriggering
                if ((Channel[i].param & 0x0f) != 0) {
                  Channel[i].ret_count --;
                  if (Channel[i].ret_count == 0) {
                    Channel[i].ret_count = Channel[i].param & 0x0f;

                    begin = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start;
                    start = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start
                          + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_start;
                    if (insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_length > 0) {
                      end = start
                      + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_length;
                    }
                    else {
                      end = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start
                      + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_length;
                    }
                    mode = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Loop_Mode;
                    UltraRestartVoice(i, begin,start,end,mode,Channel[i].volume * Channel[i].vol_val / 64,
                                      insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Is_16Bit);
                  }
                }
                break;
    case 0x0a : // fine volume slide up
                if ((Channel[i].param & 0x0f) != 0) {
                  Channel[i].last_param = Channel[i].param;
                }
                param = Channel[i].last_param & 0x0f;
                Channel[i].volume += param;
                if (Channel[i].volume > 64) {
                  Channel[i].volume = 64;
                }
                UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                Channel[i].effect = 255;
                break;
    case 0x0b : // fine volume slide down
                if ((Channel[i].param & 0x0f) != 0) {
                  Channel[i].last_param = Channel[i].param;
                }
                param = Channel[i].last_param & 0x0f;
                Channel[i].volume -= param;
                if (Channel[i].volume < 0) {
                  Channel[i].volume = 0;
                }
                UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                Channel[i].effect = 255;
                break;
    case 0x0c : // 
                break;
    case 0x0d : // 
                break;
    case 0x0e : // 
                break;
    case 0x0f : // 
                break;
  } 
}

void process_effects(unsigned short int i)
// i = Channel - Number
{
  char para;
  unsigned int begin;   
  unsigned int start;   
  unsigned int end;     
  unsigned char mode;    

   switch (Channel[i].effect) {
     case 0x01 : /// Portamento UP
                 if (Channel[i].param != 0) {
                   Channel[i].period -= Channel[i].param;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   Channel[i].period -= Channel[i].last_param;
                 }
                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_freq = 0;
                 break;
     case 0x02 : /// Portamento DOWN
                if (Channel[i].param != 0) {
                   Channel[i].period += Channel[i].param;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   Channel[i].period += Channel[i].last_param;
                 }
                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_freq = 0;
                 break;
     case 0x03 :  // Tone Portamento
                 DoTonePortamento(i);

                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_freq = 0;
                 break;

     case 0x04 :  // Vibrato
                if (Channel[i].param != 0) {
                   para = Channel[i].param;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   para = Channel[i].last_param;
                 }

                 if (para & 0x0f) Channel[i].vib_depth = para & 0x0f;
                 if (para & 0xf0) Channel[i].vib_rate = (para & 0xf0) >> 4;
                 DoVibrato(i);
                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_freq = 0;
                 break;

     case 0x05 :  // Tone Portamento & Volume Slide
                if (Channel[i].param != 0) {
                   para = Channel[i].param;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   para = Channel[i].last_param;
                 }

                 DoTonePortamento(i);

                 if (Actual_Tick == 0) {
                   if (para <= 0x0f) {
                     if ((Channel[i].volume - para) < 0) {
                       Channel[i].volume = 0;
                     }
                     else {
                       Channel[i].volume -= para;
                     }
                     UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                   }
                   else {
                     Channel[i].volume += (para >> 4);
                     if (Channel[i].volume > 64) {
                       Channel[i].volume = 64;
                     }
                     UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                   }
                 }

                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_freq = 0;
                 break;

     case 0x06 : /// Vibrato & Volume Slide
                if (Channel[i].param != 0) {
                   para = Channel[i].param;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   para = Channel[i].last_param;
                 }

                 if (para <= 0x0f) {
                   if ((Channel[i].volume - para) < 0) {
                     Channel[i].volume = 0;
                   }
                   else {
                     Channel[i].volume -= para;
                   }
                   UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                 }
                 else {
                   Channel[i].volume += (para >> 4);
                   if (Channel[i].volume > 64) {
                     Channel[i].volume = 64;
                   }
                   UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                 }
                 DoVibrato(i);
                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_freq = 0;
                 break;

     case 0x07 :  // Tremolo
                if (Channel[i].param != 0) {
                   para = Channel[i].param;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   para = Channel[i].last_param;
                 }

                 if (para & 0x0f) Channel[i].vib_depth = para & 0x0f;
                 if (para & 0xf0) Channel[i].vib_rate = (para & 0xf0) >> 4;
                 DoTremolo(i);
                 UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                 break;
     case 0x08 : /// Set Panning
                if (Channel[i].param != 0) {
                   para = Channel[i].param;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   para = Channel[i].last_param;
                 }
                 Channel[i].pan_val = para >> 2;
                 UltraSetPanning(i,Channel[i].pan_val);
                 break;
     case 0x09 : /// Play with Offsett
                 begin = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start;
                 start = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start
                   + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_start;
                 if (insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_length > 0) {
                   end = start
                         + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_length;
                 }
                 else {
                   end = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start
                         + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_length;
                 }

                 begin += Channel[i].param * 0x100;
                 mode = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Loop_Mode;
                 UltraPrimeVoice(i, begin,start,end,mode,Channel[i].volume * Channel[i].vol_val / 64,
                                 insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Is_16Bit);
                 UltraSetFrequency(i,GetFreq2(Channel[i].period));
                 replay_note = 0;
                 replay_freq = 0;
                 Channel[i].effect = 255;
                 break;
     case 0x0a : /// Volume Slide
                if (Channel[i].param != 0) {
                   para = Channel[i].param;
                   Channel[i].last_param = Channel[i].param;
                 }
                 else {
                   para = Channel[i].last_param;
                 }

                 if (para <= 0x0f) {
                   if ((Channel[i].volume - para) < 0) {
                     Channel[i].volume = 0;
                   }
                   else {
                     Channel[i].volume -= para;
                   }
                   UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                 }
                 else {
                   Channel[i].volume += (para >> 4);
                   if (Channel[i].volume > 64) {
                     Channel[i].volume = 64;
                   }
                   UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                 }
                 break;
     case 0x0b : // Position Jump
                 Break_The_Pattern = 1;
                 Songpos = Channel[i].param;
                 break;
     case 0x0c : /// Set Volume
                 if (Channel[i].param <= 0x40) {
                   Channel[i].volume = Channel[i].param;
                     UltraSetDirectVolume(i,Channel[i].volume * Channel[i].vol_val / 64);
                 }
                 Channel[i].effect = 255;
                 break; 
     case 0x0d : // Pattern Break
                 Break_The_Pattern = 1;
                 Songpos ++;
                 Channel[i].effect = 255;
                 break;
     case 0x0e : // Pattern Break
                 do_e_effects(i);
                 break;
     case 0x0f : // Set Speed
                 if (Channel[i].param >= 0x20) {
                   Set_BPM(Channel[i].param-1);
                 }
                 else {
                   Set_Speed(Channel[i].param-1);
                 }
                 Channel[i].effect = 255;
                 break;
   }
  
}

void process_patternrow()
{
 char Note;
 char dummy;
 int  tmpp;
 unsigned long begin;    /* start location in ultra DRAM */
 unsigned long start;    /* start loop location in ultra DRAM */
 unsigned long end;      /* end location in ultra DRAM */
 unsigned char mode;     /* mode to run the voice (loop etc) */
 char s_note;
 char s_inst;
 char s_vol;
 char s_eff;
 char s_para;


 for (i = 0; i < xmh.num_channels; i++) {
    Channel[i].note = 0;
    Channel[i].param = 0;
    Channel[i].veffect = 0;
    Channel[i].effect = 255;
    p_note = 0;           // Set Defaults
    p_inst = 0;
    p_vol  = 0;
    p_eff  = 0;
    p_para = 0;
    replay_note = 0;
    replay_freq = 0;
    
    Note = *pos_patt;
    pos_patt ++;

    if (Note >= 128) {                // Pack - information
      if ((Note & 1) == 1) {            // Note follows
        p_note = 1;
      }
      if ((Note & 2) == 2) {            // Instrument follows
        p_inst = 1;
      }
      if ((Note & 4) == 4) {            // Volume follows
        p_vol = 1;
      }
      if ((Note & 8) == 8) {            // Effect follows
        p_eff = 1;
      }
      if ((Note & 16) == 16) {           // Parameter follows
        p_para = 1;
      }
    }
    else
    {
        pos_patt --;
        p_note = 1;
        p_inst = 1;
        p_vol  = 1;
        p_eff  = 1;
        p_para = 1;
    }


   if (p_note == 1) {
     s_note = *pos_patt;
     pos_patt ++;
   }

   if (p_inst == 1) {

     if (*pos_patt <= xmh.num_instruments) {
       Channel[i].inst_no = *pos_patt -1;
       pos_patt ++;
   
       if (insts[Channel[i].inst_no].num_samples > 0) {
         Channel[i].smpno = insts[Channel[i].inst_no].iheader->samp_numbers[s_note-1];
         Channel[i].volume = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->volume;
         Channel[i].finetune = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->finetune;
         Channel[i].vibpos = 0;
         Channel[i].Wait_Keyoff = 0;
         Channel[i].is_pan_env = (insts[Channel[i].inst_no].iheader->pan_type & 1);
         if (Channel[i].is_pan_env == 1) {
           Channel[i].is_pan_sus = (insts[Channel[i].inst_no].iheader->pan_type & 2) - 1;
           Channel[i].is_pan_loop = (insts[Channel[i].inst_no].iheader->pan_type & 4) - 3;
           Channel[i].pan_pt1 = 0;
           Channel[i].pan_pt2 = 1;
           Channel[i].pan_pos = 0;
           Channel[i].pan_val = insts[Channel[i].inst_no].iheader->pan_envelope[1];
         }
         else {
           Channel[i].pan_val = 32;
         }
  
         Channel[i].is_vol_env = (insts[Channel[i].inst_no].iheader->vol_type & 1);
         if (Channel[i].is_vol_env == 1) {
           Channel[i].is_vol_sus = (insts[Channel[i].inst_no].iheader->vol_type & 2) - 1;
           Channel[i].is_vol_loop = (insts[Channel[i].inst_no].iheader->vol_type & 4) - 3;
           Channel[i].vol_pt1 = 0;
           Channel[i].vol_pt2 = 1;
           Channel[i].vol_pos = 0;
           Channel[i].vol_val = insts[Channel[i].inst_no].iheader->vol_envelope[1];
         }
         else {
           Channel[i].vol_val = 64;
         }
       }
       else { // No sample data
         Channel[i].smpno = 0;
         Channel[i].volume = 0;
         Channel[i].finetune = 0;
         Channel[i].vibpos = 0;
         Channel[i].Wait_Keyoff = 0;
         Channel[i].is_pan_env = 0;
         Channel[i].pan_val = 32;
         Channel[i].is_vol_env = 0;
         UltraSetDirectVolume(i,0);
         replay_note = 0;
         replay_freq = 0;
       }
     }
     else {
       pos_patt ++;
     }
   }

   if (p_vol == 1) {
     Channel[i].veffect = *pos_patt;
     pos_patt ++;
   }

   if (p_eff == 1) {
     Channel[i].effect = *pos_patt;
     pos_patt ++;
   }

   if (p_para == 1) {
     Channel[i].param = *pos_patt;
     pos_patt ++;
   }


   if (p_note == 1) {
     Channel[i].dest_note = Channel[i].last_note;
     Channel[i].note = s_note-0;

     if (Channel[i].note >= 95) {
       if (Channel[i].is_vol_sus == 1) {   //  Keyoff active ?
         Channel[i].is_vol_sus = 0;
         Channel[i].Wait_Keyoff = 0;
       }
       else {
         Channel[i].vol_val = 0;
         UltraSetDirectVolume(i,0);
         replay_note = 0;
         replay_freq = 0;
       }
     }
     else {
       if ((Channel[i].effect == 3) || (Channel[i].effect == 5)) {
         tmpp = Channel[i].period;
         Channel[i].dest_period = get_freq(i);
         Channel[i].note = Channel[i].dest_note;
         Channel[i].period = tmpp;
       }
       else {
         Channel[i].period = get_freq(i);
       }
       Channel[i].last_note = Channel[i].note;
       Channel[i].ret_count = Channel[i].param & 0x0f;
       replay_note = 1;
       replay_freq = 1;
     }
   }


   process_volume_effects(i);
   process_effects(i); 

   if ((Channel[i].note < 120) && (Channel[i].note > 0)) {

       begin = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start;
       start = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start
                 + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_start;
       if (insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_length > 0) {
         end = start
               + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_loop_length;
       }
       else {
         end = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Gus_Start
               + insts[Channel[i].inst_no].sheader[Channel[i].smpno]->sample_length;
       }
       mode = insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Loop_Mode;


     if (replay_note == 1) {
       UltraPrimeVoice(i, begin,start,end,mode,Channel[i].volume * Channel[i].vol_val / 64,insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Is_16Bit);
     }

     if (replay_freq == 1) {
       UltraPrimeVoice(i, begin,start,end,mode,Channel[i].volume * Channel[i].vol_val / 64,insts[Channel[i].inst_no].sheader[Channel[i].smpno]->Is_16Bit);
       UltraSetFrequency(i,GetFreq2(Channel[i].period));
     }

   }
 } // end for



 Row ++;

 if (Break_The_Pattern == 1) {
   if (Songpos > xmh.song_length-1) {
    Songpos = xmh.restart_position;
   }
   NewPattern(xmh.pat_table[Songpos]);
 }

 if (Row > (maxrows -1)) {
   Songpos ++;
   if (Songpos > xmh.song_length-1) {
    Songpos = xmh.restart_position;
   }
   NewPattern(xmh.pat_table[Songpos]);
 }

 Break_The_Pattern = 0;
 row_changed = 1;
}


