#include <windows.h>
#include <mmsystem.h>
#include "main.h"

#define Max4kChannels 16;

__attribute__((packed)) struct T4kFileData {
 unsigned char WaveForm[Max4kChannels];
 unsigned char Link[Max4kChannels];
 float LinkFactor[Max4kChannels];
 float OutFactor[Max4kChannels];
 float StartVolume[Max4kChannels];
 float StartVolumeFactor[Max4kChannels];
 float StartPhaseFactor[Max4kChannels];
 unsigned char Panning[Max4kChannels];
 int EventOffset[Max4kChannels];
 int NoteOffset[Max4kChannels];
 int VolumeOffset[Max4kChannels];
};

__attribute__((packed)) struct T4kFilter {
 float FD1;
 float FD2;
 float FD3;
 float FD4;
 float FBLP;
 float FBHP;
 float CLP;
 float CHP;
 float RLP;
 float RHP; 
}; 

__attribute__((packed)) struct T4kADSRData {
 float AttackStep[Max4kChannels];
 float DecayStep[Max4kChannels];
 float DestDecay[Max4kChannels];
 unsigned char Sustain[Max4kChannels];
 float ReleaseStep[Max4kChannels];
};

__attribute__((packed)) struct T4kRealTimeData {
 unsigned char LinkFirst[Max4kChannels];
 float LinkValues[Max4kChannels];
 float Volume[Max4kChannels];
 float VolumeValue[Max4kChannels];
 float VolumeFactor[Max4kChannels];
 float ChannelLastLeft[Max4kChannels];
 float ChannelLastRight[Max4kChannels];
 int ADSRMode[Max4kChannels];
 float ADSRValue[Max4kChannels];
 float Phase[Max4kChannels];
 float PhaseIncrement[Max4kChannels];
 float PhaseFactor[Max4kChannels];
 T4kFilter Filter[Max4kChannels];
 float LastLeft;
 float LastRight;
 int TickCounter[Max4kChannels];
 unsigned char *EventOffset[Max4kChannels];
 unsigned char *NoteOffset[Max4kChannels];
 unsigned char *VolumeOffset[Max4kChannels];
 unsigned char *RestartEventOffset[Max4kChannels];
 unsigned char *RestartNoteOffset[Max4kChannels];
 unsigned char *RestartVolumeOffset[Max4kChannels];
 unsigned int BPMSamples;
 unsigned int CurrentBPMSamples;
 unsigned char ShouldExit;
}

typedef unsigned char T4kEventData[0x200000];

__attribute__((packed)) struct T4kSynthData {
 T4kFileData FileData;
 T4kADSRData ADSRData;
 unsigned int InfoData;
 T4kEventData EventData;
 T4kRealtimeData RealtimeData;
}

#define SampleRate 44100
#define SampleRateFactor (float)(440/SampleRate)
#define PortamentoFactor (float)(SampleRateFactor/16)
#define Div8 (float)(1/8)
#define Div12 (float)(1/12)
#define Div64 (float)(1/64)
#define Div127 (float)(1/127)
#define Div255 (float)(1/255)
#define Div256 (float)(1/256)
#define Div65536 (float)(1/65536)
#define CutOffLowPassStartValue (float)(255*Div256)
#define BufferSize 096

WAVEFORMATEX WaveFormat=(3,2,SampleRate,SampleRate*8,8,32,0);

HWAVEOUT WaveOutHandle;
WAVEHDR WaveHandler[4];
unsigned int BufferCounter;
unsigned int WhiteNoiseSeed;
float Buffers[4][BufferSize][2];

void FILLCHAR(unsigned char *dst,int count,unsigned char value){
 while(count--)*(dst++)=value;
}  

void MOVE(unsigned char *src,unsigned char *dst,int count){
 while(count--)*(dst++)=*(src++);
}  

float F_POWER(float Number,float Exponent){
 float value;   
 __asm__ ("fld %0":"=m"(*(float*)(&exponent))); /* flds */
 __asm__ ("fld %0":"=m"(*(float*)(&number))); /* flds */ 
 __asm__ ("fyl2x"); 
 __asm__ ("fld1"); 
 __asm__ ("fld %st(1)"); 
 __asm__ ("fprem"); 
 __asm__ ("f2xm1"); 
 __asm__ ("faddp"); /*__asm__ ("faddp %st,%st(1)"); */
 __asm__ ("fscale"); 
 __asm__ ("fstp %st(1)"); 
 __asm__ ("fstp %0":"=m"(*(float*)(&value)));  /* fsts */
 return value;
}  

float WhiteNoiseRandom(){
 unsigned int WhiteNoiseValue;
 WhiteNoiseSeed=(WhiteNoiseSeed*0x524281)+0x3133731;
 WhiteNoiseValue=(WhiteNoiseSeed&0x7FFFFF)|0x40000000;
 return (float*)&WhiteNoiseValue;
}

float FRAC(float X);
 float Y=X;
 float value;   
 __asm__ ("fld %0":"=m"(*(float*)(&Y))); /* flds */
 __asm__ ("frndint"); 
 __asm__ ("fstp %0":"=m"(*(float*)(&Y)));  /* fsts */
 return X-Y;
}

void SynthReinit(T4kSynthData *SynthData){
 int Channel;
 for(Channel=0;Channel<Max4kChannels;Channel++){
  SynthData->RealtimeData.EventOffset[Channel]=SynthData->RealtimeData.RestartEventOffset[Channel];
  SynthData->RealtimeData.NoteOffset[Channel]=SynthData->RealtimeData.RestartNoteOffset[Channel];
  SynthData->RealtimeData.VolumeOffset[Channel]=SynthData->RealtimeData.RestartVolumeOffset[Channel];
  SynthData->RealtimeData.TickCounter[Channel]=0; 
 } 
 SynthData->RealtimeData.ShouldExit=1;
}

void SynthRecalcFilter(T4kFilter *Filter){
 Filter->FBLP=Filter->RLP+Filter->RLP/(1-Filter->CLP);
 Filter->FBHP=Filter->RHP+Filter->RHP/(1-Filter->CLP);
}

void SynthInitData(T4kSynthData *SynthData) {
 int Channel;
 for(Channel=0;Channel<Max4kChannels;Channel++){
  if(SynthData->FileData.EventOffset[Channel]){
   SynthData->RealtimeData.RestartEventOffset[Channel]:=((int*)(&SynthData))+SynthData->FileData.EventOffset[Channel];
   SynthData->RealtimeData.RestartNoteOffset[Channel]:=((int*)(&SynthData))+SynthData->FileData.NoteOffset[Channel];
   SynthData->RealtimeData.RestartVolumeOffset[Channel]:=((int*)(&SynthData))+SynthData->FileData.VolumeOffset[Channel];
  }
  SynthData->RealtimeData.Filter[Channel].CLP=CutOffLowPassStartValue;
  SynthRecalcFilter(&SynthData->RealtimeData.Filter[Channel]);
 } 
 SynthReinit(SynthData);
 SynthData->RealtimeData.ShouldExit=0;
}

void SynthCalcNote(T4kSynthData *SynthData;int Channel,int Note){
 SynthData->RealtimeData.PhaseIncrement[Channel]:=F_POWER(2,(Note-45)*Div12)*SampleRateFactor;
}

void SynthFillBuffer(T4kSynthData *SynthData,float *Buffer,int StartPosition,int BufferSize){
 int Position,Channel,Count,WaveForm,PhaseCasted;
 float Phase,Left,Right,PanningValue,OscValue,Value;
 unsigned char Note,Volume;
 T4kFilter *Filter;
 for(Position=0;Position<BufferSize;Position++){
  Count=Max4kChannels;
  Left=0;
  Right=0;
  FILLCHAR(&SynthData->RealtimeData.LinkFirst,16,1);
  for(Channel=0;Channel<Max4kChannels;Channel++){
   if(!SynthData->RealtimeData.CurrentBPMSamples){
    while((SynthData->RealtimeData.EventOffset[Channel])&&(SynthData->RealtimeData.TickCounter[Channel]>=*SynthData->RealtimeData.EventOffset[Channel]^)){
     SynthData->RealtimeData.TickCounter[Channel]=0;
     Note=SynthData->RealtimeData.NoteOffset[Channel]^;
     Volume=SynthData->RealtimeData.VolumeOffset[Channel]^;
     SynthData->RealtimeData.EventOffset[Channel]++;
     SynthData->RealtimeData.NoteOffset[Channel]++;
     SynthData->RealtimeData.VolumeOffset[Channel]++;
     if(Note&&(Note<=0x81)){
      if(Note<0x80){
       SynthData->RealtimeData.LastLeft=SynthData->RealtimeData.LastLeft+SynthData->RealtimeData.ChannelLastLeft[Channel];
       SynthData->RealtimeData.LastRight=SynthData->RealtimeData.LastRight+SynthData->RealtimeData.ChannelLastRight[Channel];
       SynthData->RealtimeData.ChannelLastLeft[Channel]=0;
       SynthData->RealtimeData.ChannelLastRight[Channel]=0;
       SynthCalcNote(SynthData,Channel,Note);
       SynthData->RealtimeData.PhaseFactor[Channel]=SynthData.FileData.StartPhaseFactor[Channel];
       SynthData->RealtimeData.Volume[Channel]=SynthData.FileData.StartVolume[Channel];
       SynthData->RealtimeData.VolumeFactor[Channel]=SynthData.FileData.StartVolumeFactor[Channel];
       SynthData->RealtimeData.ADSRMode[Channel]=1;
       SynthData->RealtimeData.ADSRValue[Channel]=0;
      }else if(Note==0x80){
       SynthData->RealtimeData.ADSRMode[Channel]=4;
      }     
      if(Volume<=64)SynthData->RealtimeData.VolumeValue[Channel]=Volume*Div64;
     }else{    
      Value=Volume*Div256;
      select(Note){
       case 0x82:
        SynthData.RealtimeData.BPMSamples:=(SampleRate*5*128)/(Volume>>8);
        break;
       case 0x83:
        SynthData.RealtimeData.EventOffset[Channel]=0;
        break;
       case 0x84:case 0x85:
        OscValue=Volume*PortamentoFactor;
        if(Note==0x84)OscValue=-OscValue;
        SynthData->RealtimeData.PhaseIncrement[Channel]=SynthData.RealtimeData.PhaseIncrement[Channel]*(1+OscValue)
        break;
       case 0x86:
        SynthData->FileData.Panning[Channel]=Volume;
        break;
       case 0x87:
        SynthData->RealtimeData.Filter[Channel].CLP=Value;
        SynthRecalcFilter(&SynthData.RealtimeData.Filter[Channel]);
        break;
       case 0x88:
        SynthData->RealtimeData.Filter[Channel].CHP=Value
        SynthRecalcFilter(&SynthData.RealtimeData.Filter[Channel]);
        break;
       case 0x89:
        SynthData->RealtimeData.Filter[Channel].RLP:=Value;
        SynthRecalcFilter(&SynthData->RealtimeData.Filter[Channel]);
        break;
       case 0x90:
        SynthData->RealtimeData.Filter[Channel].RHP:=Value;
        SynthRecalcFilter(&SynthData->RealtimeData.Filter[Channel]);
        break;
       case 0x91:
        SynthData->RealTimeData.Phase[Channel]:=Value*2*PI;
        break;
       case 0x92:
        SynthCalcNote(SynthData,Channel,Volume);
        break;
       case 0x93:
        SynthData->RealtimeData.RestartEventOffset[Channel]:=SynthData->RealtimeData.EventOffset[Channel];
        SynthData->RealtimeData.RestartNoteOffset[Channel]:=SynthData->RealtimeData.NoteOffset[Channel];
        SynthData->RealtimeData.RestartVolumeOffset[Channel]:=SynthData->RealtimeData.VolumeOffset[Channel];
        break;
      }      
     }    
    }
    if(!SynthData->RealtimeData.EventOffset[Channel])Count--;
    SynthData->RealtimeData.TickCounter[Channel]++;
   }   
   Phase=FRAC(SynthData->RealtimeData.Phase[Channel]+SynthData->RealtimeData.LinkValues[Channel]);
   WaveForm=SynthData->FileData.WaveForm[Channel];
   select(WaveForm){
    case 0: /* Sinus */
     OscValue=sin(Phase*2.0*PI); 
     break;
    case 1: /* Triangle */
     OscValue=abs((Phase-0.5)*4.0)-1.0; 
     break;
    case 2: /* Square */ 
     Phase-=0.5;
     PhaseCasted=(((int*)&Phase)>>31)<<1;
     OscValue=1.0-(float)PhaseCasted;
     break;
    case 3:case 4: /* Sawtooth Up/Down */
     OscValue=((Phase-0.5)*2.0)*(float)(1-((WaveForm-3)*2)); 
     break;
    case 5: /* White Noise */ 
     OscValue=WhiteNoiseRandom();
     break;
    default: /* Nothing ;-) */
     OscValue=0; 
     break;
   }     
   OscValue=OscValue*SynthData->RealtimeData.Volume[Channel]*SynthData->RealtimeData.VolumeValue[Channel]*SynthData->RealtimeData.ADSRValue[Channel];
   Filter=&SynthData->RealtimeData.Filter[Channel];
   Filter->FD1=Filter->FD1+Filter->CLP*(OscValue-Filter->FD1+Filter->FBLP*(Filter->FD1-Filter->FD2));
   Filter->FD2=Filter->FD2+Filter->CLP*(Filter->FD1-Filter->FD2);
   Filter->FD3=Filter->FD3+Filter->CHP*(Filter->FD2-Filter->FD3+Filter->FBHP*(Filter->FD3-Filter->FD4));
   Filter->FD4=Filter->FD4+Filter->CHP*(Filter->FD3-Filter->FD4);
   OscValue=Filter->FD2-Filter->FD4;
   select(SynthData->RealtimeData.ADSRMode[Channel]){
    case 0:
     break;      
    case 1:
     if(SynthData->RealtimeData.ADSRValue[Channel]<1){
      SynthData->RealtimeData.ADSRValue[Channel+=SynthData->ADSRData.AttackStep[Channel];
     }else{
      SynthData->RealtimeData.ADSRValue[Channel]=1;
      SynthData->RealtimeData.ADSRMode[Channel]=2;
     }
    }
    case 2:
     if(SynthData->RealtimeData.ADSRValue[Channel]>SynthData->ADSRData.DestDecay[Channel]){
      SynthData->RealtimeData.ADSRValue[Channel]+=SynthData.ADSRData.DecayStep[Channel];
     }else{
      SynthData->RealtimeData.ADSRMode[Channel]=3+(SynthData->ADSRData.Sustain[Channel]?1:0);
     }
     break;
    case 3:
     break;
    case 4:
     if(SynthData->RealtimeData.ADSRValue[Channel]>0){
      SynthData->RealtimeData.ADSRValue[Channel]+=SynthData->ADSRData.ReleaseStep[Channel];
     }else{
      SynthData->RealtimeData.ADSRMode[Channel]=0;
     }
     break;
   }
   if(SynthData->RealtimeData.ADSRValue[Channel]<0)SynthData->RealtimeData.ADSRValue[Channel]=0;
   if(SynthData->RealtimeData.ADSRValue[Channel]>1)SynthData->RealtimeData.ADSRValue[Channel]=1;
   int LinkChannel=SynthData->FileData.Link[Channel]&0x7F;
   if(LinkChannel<Max4kChannels){
    if(SynthData->RealtimeData.LinkFirst[LinkChannel]){
     SynthData->RealtimeData.LinkValues[LinkChannel]=0;
     SynthData->RealtimeData.LinkFirst[LinkChannel]=0;
    }
    SynthData->RealtimeData.LinkValues[LinkChannel]+=OscValue*SynthData->FileData.LinkFactor[Channel];
   }   
   if(SynthData.FileData.Link[Channel]&0x80){
    Value=OscValue*SynthData->FileData.OutFactor[Channel];
    PanningValue=SynthData->FileData.Panning[Channel]*Div255;
    OscValue=Value*PanningValue;
    SynthData->RealtimeData.ChannelLastLeft[Channel]=OscValue;
    Left+=OscValue;
    OscValue=Value*(1-PanningValue);
    SynthData->RealtimeData.ChannelLastRight[Channel]=OscValue;
    Right+=OscValue;
   }
   SynthData->RealtimeData.Phase[Channel]=FRAC(SynthData->RealtimeData.Phase[Channel]+SynthData->RealtimeData.PhaseIncrement[Channel]);
   SynthData->RealtimeData.PhaseIncrement[Channel]*=SynthData->RealtimeData.PhaseFactor[Channel];
   SynthData->RealtimeData.Volume[Channel]*=SynthData->RealtimeData.VolumeFactor[Channel];
  }
  if(!SynthData.RealtimeData.CurrentBPMSamples){
   if(!Count)SynthReinit(SynthData);
   SynthData->RealtimeData.CurrentBPMSamples=SynthData->RealtimeData.BPMSamples;
  }
  SynthData->RealtimeData.CurrentBPMSamples--;
  Left+=SynthData.RealtimeData.LastLeft;
  SynthData.RealtimeData.LastLeft*=0.9;
  Right+=SynthData.RealtimeData.LastRight;
  SynthData.RealtimeData.LastRight*=0.9;
  *(Buffer++)=Left*Div8;
  *(Buffer++)=Right*Div8;
 } 
}

void SynthLoad(T4kSynthData *SynthData,unsigned char *TrackData,int TrackSize){
 FILLCHAR(SynthData,sizeof(T4kSynthData),0);
 MOVE(TrackData,SynthData,TrackSize);
 SynthInitData(SynthData);
}

void SynthInit(T4kSynthData *SynthData,unsigned char *TrackData,int TrackSize){
 static unsigned short int cwChop=0xF7B;
 int i;
 __asm__ ("fldcw %0":"=m"(*(unsigned short int*)(&cwChop)));

 SynthLoad(SynthData,TrackData,TrackSize);

 WhiteNoiseSeed=$12345678;

 waveOutOpen(@WaveOutHandle,WAVE_MAPPER,@WaveFormat,0,0,0);

 for(i=0;i<4;i++){
  WaveHandler[I].dwFlags=WHDR_DONE;
  WaveHandler[I].lpData=@Buffers[I];
  WaveHandler[I].dwBufferLength=BufferSize*8;
 }

 BufferCounter=0;
}

void SynthPoll(T4kSynthData *SynthData){
 if(WaveHandler[BufferCounter].dwFlags&WHDR_DONE){
  if(waveOutUnprepareHeader(WaveOutHandle,@WaveHandler[BufferCounter],sizeof(TWAVEHDR))<>WAVERR_STILLPLAYING){
   WaveHandler[BufferCounter].dwFlags=WaveHandler[BufferCounter].dwFlags&~WHDR_DONE;
   SynthFillBuffer(SynthData,WaveHandler[BufferCounter].lpData,0,BufferSize);
   waveOutPrepareHeader(WaveOutHandle,&WaveHandler[BufferCounter],sizeof(TWAVEHDR));
   waveOutWrite(WaveOutHandle,@WaveHandler[BufferCounter],sizeof(TWAVEHDR));
   BufferCounter=(BufferCounter+1)&3;
  }
 }
}

void SynthDone(T4kSynthData *SynthData){
#ifdef CLEAN
 int i;
 for(i=0;i<4;i++)while(waveOutUnprepareHeader(WaveOutHandle,&WaveHandler[I],sizeof(TWAVEHDR))=WAVERR_STILLPLAYING);
 waveOutReset(WaveOutHandle);
 waveOutClose(WaveOutHandle);
#endif
}

