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

WAVEFORMATEX WaveFormat;
HWAVEOUT WaveOutHandle;
WAVEHDR WaveHandler[4];
unsigned int BufferCounter;
unsigned int WhiteNoiseSeed;
unsigned char Buffers[4*BufferSize*8];

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;
 float *cast;
 WhiteNoiseSeed=(WhiteNoiseSeed*0x524281)+0x3133731;
 WhiteNoiseValue=(WhiteNoiseSeed&0x7FFFFF)|0x40000000;
 cast=&WhiteNoiseValue;
 return *cast;
}

float FRAC(float X){
 return X-(int)X;      
}

void SynthReinit(struct 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(struct T4kFilter *Filter){
 Filter->FBLP=Filter->RLP+Filter->RLP/(1.0f-Filter->CLP);
 Filter->FBHP=Filter->RHP+Filter->RHP/(1.0f-Filter->CLP);
}

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

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

void SynthFillBuffer(struct T4kSynthData *SynthData,float *Buffer,int StartPosition,int TheBufferSize){
 int Position,Channel,Count,WaveForm;
 float Phase,Left,Right,PanningValue,OscValue,Value,TempPhase;
 unsigned char Note,Volume;
 unsigned int PhaseCasted,TempPhaseCasted;
 struct T4kFilter *Filter;
 for(Position=0;Position<TheBufferSize;Position++){
  Count=Max4kChannels;
  Left=0.0f;
  Right=0.0f;
  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<=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.0f;
       SynthData->RealtimeData.ChannelLastRight[Channel]=0.0f;
       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.0f;
      }else if(Note==0x80){
       SynthData->RealtimeData.ADSRMode[Channel]=4;
      }     
      if(Volume<=64)SynthData->RealtimeData.VolumeValue[Channel]=(float)Volume*Div64;
     }else{    
      Value=(float)Volume*Div256;
      switch(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=(float)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];
   switch(WaveForm){
    case 0: /* Sinus */
     OscValue=sin(Phase*TWOPI); 
     break;
    case 1: /* Triangle */
     TempPhase=(Phase-0.5f)*4.0f;
     TempPhaseCasted=(*(unsigned int*)&TempPhase)&0x7FFFFFFF;
     OscValue=(*(float*)&TempPhaseCasted)-1.0f; 
     break;
    case 2: /* Square */ 
     TempPhase=Phase-0.5f;
     OscValue=1.0f-(((*(unsigned int*)&TempPhase)>>31)<<1);
     break;
    case 3:case 4: /* Sawtooth Up/Down */
     OscValue=((Phase-0.5f)*2.0f)*(float)(1-((WaveForm-3)*2)); 
     break;
    case 5: /* White Noise */ 
     OscValue=WhiteNoiseRandom();
     break;
    default: /* Nothing ;-) */
     OscValue=0.0f; 
     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;
   switch(SynthData->RealtimeData.ADSRMode[Channel]){
    case 0:
     break;      
    case 1:
     if(SynthData->RealtimeData.ADSRValue[Channel]<1.0f){
      SynthData->RealtimeData.ADSRValue[Channel]+=SynthData->ADSRData.AttackStep[Channel];
     }else{
      SynthData->RealtimeData.ADSRValue[Channel]=1.0f;
      SynthData->RealtimeData.ADSRMode[Channel]=2;
     }
     break;
    case 2:
     if(SynthData->RealtimeData.ADSRValue[Channel]>SynthData->ADSRData.DestDecay[Channel]){
      SynthData->RealtimeData.ADSRValue[Channel]+=SynthData->ADSRData.DecayStep[Channel];
     }else{
      SynthData->RealtimeData.ADSRMode[Channel]=4-(SynthData->ADSRData.Sustain[Channel]?1:0);
     }
     break;
    case 3:
     break;
    case 4:
     if(SynthData->RealtimeData.ADSRValue[Channel]>0.0f){
      SynthData->RealtimeData.ADSRValue[Channel]+=SynthData->ADSRData.ReleaseStep[Channel];
     }else{
      SynthData->RealtimeData.ADSRMode[Channel]=0.0f;
     }
     break;
   }
   if(SynthData->RealtimeData.ADSRValue[Channel]<0.0f)SynthData->RealtimeData.ADSRValue[Channel]=0.0f;
   if(SynthData->RealtimeData.ADSRValue[Channel]>1.0f)SynthData->RealtimeData.ADSRValue[Channel]=1.0f;
   int LinkChannel=SynthData->FileData.Link[Channel]&0x7F;
   if(LinkChannel<Max4kChannels){
    if(SynthData->RealtimeData.LinkFirst[LinkChannel]){
     SynthData->RealtimeData.LinkValues[LinkChannel]=0.0f;
     SynthData->RealtimeData.LinkFirst[LinkChannel]=0.0f;
    }
    SynthData->RealtimeData.LinkValues[LinkChannel]+=OscValue*SynthData->FileData.LinkFactor[Channel];
   }   
   if(SynthData->FileData.Link[Channel]&0x80){
    Value=OscValue*SynthData->FileData.OutFactor[Channel];
    PanningValue=(float)(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.9f;
  Right+=SynthData->RealtimeData.LastRight;
  SynthData->RealtimeData.LastRight*=0.9f;
  *(Buffer++)=Left*Div8;
  *(Buffer++)=Right*Div8;
 } 
}

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

void SynthInit(struct 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=0x12345678;

 WaveFormat.wFormatTag=3;
 WaveFormat.nChannels=2;
 WaveFormat.wBitsPerSample=32;
 WaveFormat.nBlockAlign=8;
 WaveFormat.nSamplesPerSec=SampleRate;
 WaveFormat.nAvgBytesPerSec=SampleRate*8;
 WaveFormat.cbSize=0;      

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

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

 BufferCounter=0;
}

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

void SynthDone(struct 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
}
