/* AudioCmp.c Audio Compression Routines
   By Sébastien Métrot 
*/


#include <windows.h>
#include <mmsystem.h>
#include <winsock.h>

#include <stdio.h>
#include <stdarg.h>

/*
#include "../kernel/mmemory.h"
#include "../kernel/listlab.h"
#include "../kernel/mbytec.h" 
#include "../kernel/scol.h"
#include "../kernel/fifo.h"
#include "myloop.h"
*/
#include "x/Version.h"
#include "x/scolplugin.h"



#include "sound/audiocmp.h"

// Include the gsm prototypes for the functions in gsmcodec.lib 
#include "gsm.h"

/*
   4 bits Crunch / Decrunch 
*/
/* Fun [S] S */
int AudioCrunch4bit (mmachine m)
{
  int source;
  int len,i;
  char *S,*D;
  int dest;

  source=MMget(m,0)>>1;
  if (source==NIL) { MMset(m,0,NIL);return 0;}
  len=MMsizestr(m,source);
  len=len&0xFFFFFFFE; // should be even to work

  dest=MMmalloc(m,(((len>>1)+5)>>2)+1,TYPEBUF);
  source=MMget(m,0)>>1;
  S=MMstartstr(m,source);
  MMset(m,0,NIL);
  if (dest==NIL) return 0;
  MMstore(m,dest,0,(len>>1));
  D=MMstartstr(m,dest);
  if (D==NULL) return 0;

  for (i=0;i<(len>>1);i++)
  {
    D[i]=S[i*2]&0xF0;
    D[i]|=(S[i*2+1]>>4)&0xF;
  }
  D[i]=0;
  MMset(m,0,(dest<<1)|1);
  return 0;
}

/* Fun [S] S */
int AudioDecrunch4bit (mmachine m)
{
  int source;
  int len,i;
  char *S,*D;
  int dest;

  source=MMget(m,0)>>1;
  if (source==NIL) { MMset(m,0,NIL);return 0;}
  len=MMsizestr(m,source);

  dest=MMmalloc(m,(((len*2)+5)>>2)+1,TYPEBUF);

  source=MMget(m,0)>>1;
  MMset(m,0,NIL);
  S=MMstartstr(m,source);
  
  if (dest==NIL) return 0;
  MMstore(m,dest,0,(len*2));
  D=MMstartstr(m,dest);
  if (D==NULL) return 0;

  for (i=0;i<(len);i++)
  {
    D[i*2]=S[i]&0xF0;
    D[i*2+1]=(S[i]<<4)&0xF0;
  }
  D[i*2]=0;

  MMset(m,0,(dest<<1)|1);
  return 0;
}


/*
   2 bits Crunch / Decrunch 
*/
int AudioCrunch2bit (mmachine m)
{
  int source;
  int len,i;
  char *S,*D;
  int dest;

  source=MMget(m,0)>>1;
  if (source==NIL) { MMset(m,0,NIL);return 0;}
  len=MMsizestr(m,source);
  len=len&0xFFFFFFFC; // should be even4 to work


  dest=MMmalloc(m,(((len/4)+5)>>2)+1,TYPEBUF);
  source=MMget(m,0)>>1;
  S=MMstartstr(m,source);
  MMset(m,0,NIL);
  
  if (dest==NIL) return 0;
  MMstore(m,dest,0,(len/4));
  D=MMstartstr(m,dest);
  if (D==NULL) return 0;

  for (i=0;i<(len/4);i++)
  {
    D[i] =S[i*4]&0xC0;
    D[i]|=(S[i*4+1]>>2)&0x30;
    D[i]|=(S[i*4+2]>>4)&0x0C;
    D[i]|=(S[i*4+3]>>6)&0x03;
  }
  D[i]=0;

  MMset(m,0,(dest<<1)|1);
  return 0;
}

/* Fun [S] S */
int AudioDecrunch2bit (mmachine m)
{
  int source;
  int len,i;
  char *S,*D;
  int dest;

  source=MMget(m,0)>>1;
  if (source==NIL) { MMset(m,0,NIL);return 0;}
  len=MMsizestr(m,source);

  dest=MMmalloc(m,(((len*4)+5)>>2)+1,TYPEBUF);
  source=MMget(m,0)>>1;
  S=MMstartstr(m,source);
  MMset(m,0,NIL);
  if (dest==NIL) return 0;
  MMstore(m,dest,0,(len*4));
  D=MMstartstr(m,dest);
  if (D==NULL) return 0;

  for (i=0;i<(len);i++)
  {
    D[i*4]   =(S[i]   )&0xC0;
    D[i*4+1]|=(S[i]<<2)&0xC0;
    D[i*4+2]|=(S[i]<<4)&0xC0;
    D[i*4+3]|=(S[i]<<6)&0xC0;
  }
  D[i*4]=0;

  MMset(m,0,(dest<<1)|1);
  return 0;
}

/*
   Uninterpolate / Interpolate
*/
int AudioUnInterpol2 (mmachine m)
{
  int source;
  int len,i;
  char *S,*D;
  int dest;

  source=MMget(m,0)>>1;
  if (source==NIL) { MMset(m,0,NIL);return 0;}
  len=MMsizestr(m,source);
  len=len&0xFFFFFFFE; // should be even2 to work

  dest=MMmalloc(m,(((len/2)+5)>>2)+1,TYPEBUF);
  source=MMget(m,0)>>1;
  S=MMstartstr(m,source);
  MMset(m,0,NIL);
  if (dest==NIL) return 0;
  MMstore(m,dest,0,(len/2));
  D=MMstartstr(m,dest);
  if (D==NULL) return 0;

  for (i=0;i<(len/2);i++)
  {
    D[i] =S[i*2];
  }
  D[i]=0;

  MMset(m,0,(dest<<1)|1);
  return 0;
}

/* Fun [S] S */
int AudioInterpol2 (mmachine m)
{
  int source;
  int len,i;
  unsigned char *S,*D;
  int dest;

  source=MMget(m,0)>>1;
  if (source==NIL) { MMset(m,0,NIL);return 0;}
  len=MMsizestr(m,source);

  dest=MMmalloc(m,(((len*2)+5)>>2)+1,TYPEBUF);
  source=MMget(m,0)>>1;
  S=MMstartstr(m,source);
  MMset(m,0,NIL);
  if (dest==NIL) return 0;
  MMstore(m,dest,0,(len*2));
  D=MMstartstr(m,dest);
  if (D==NULL) return 0;

  for (i=0;i<(len);i++)
  {
    D[i*2]  =S[i];
    D[i*2+1]=(((int)S[i])+((int)S[i+1]))/2;
  }
  D[i*2-1]=S[i-1];
  D[i*2]=0;

  MMset(m,0,(dest<<1)|1);
  return 0;   
}


/* GSM function */

gsm gsmhandle;
gsm_frame gsmbuf;
gsm_signal gsmsample[160];
int NoiseLevel=50;

/* fun [] GsmCodec */
int AudioGsmInit (mmachine m)
{
  int s;

  gsmhandle=gsm_create();

  if (gsmhandle)
  {  
    s=MMmalloc(m,1,TYPEBUF);
    if (s==NIL) 
    {
      MMpush(m,NIL);
      return MERRMEM;
    }
    MMstore(m,s,0,(int)gsmhandle);

    return MMpush(m,(s<<1|1));
  }
  else 
  {
    return MMpush(m,NIL);
  }
}

/* fun [GsmCodec] I */
int AudioGsmDestroy (mmachine m)
{
  int s;
  s=MMget(m,0)>>1;
  if (s==NIL) 
  {
    MMset(m,0,0);
    return 0;
  }
  gsmhandle=(gsm)MMfetch(m,s,0);
  if (gsmhandle==NULL)
  {
    MMset(m,0,0);
    return 0;
  }
  gsm_destroy(gsmhandle);
  MMset(m,0,2);
  return 0;
}

/* 8 bits =======================================================*/
int isemptybuf8(char *s,int level,int size)
{
  int i=0;
  int total=0;
  if (!size) 
  {
    return 1;
  }
  while (i<size) total+=abs((signed char)s[i++]-128);
  if ((total/size)<level) return 1;
  return 0;
}

/* fun [S GsmCodec] S */
int AudioGsmCrunch (mmachine m)
{
  int src,dest,n,codec,i,j,slen,offset=0,rsize=0;
  char *Src,*Dest;

  codec=MMpull(m)>>1;
  src=MMget(m,0)>>1;
  if (src==NIL) { MMset(m,0,NIL);return 0;}
  if (codec==NIL) { MMset(m,0,NIL);return 0;}
  slen=MMsizestr(m,src);
  gsmhandle=(gsm)MMfetch(m,codec,0);
  if (gsmhandle==NULL) { MMset(m,0,NIL);return 0;}

//  if (slen%160) return 0;
  n=slen/160;
  dest=MMmalloc(m,((n*34)>>2)+1,TYPEBUF);
  src=MMget(m,0)>>1;
  Src=MMstartstr(m,src);
  if (Src==NULL) return 0;
  MMset(m,0,NIL);
  if (dest==NIL) return 0;
  Dest=MMstartstr(m,dest);
  for (i=0;i<n;i++)
  {
    if (isemptybuf8(&Src[i*160],NoiseLevel,160)) 
    {
      Dest[offset]=0;
      offset++;
      rsize++;
    }
    else
    {
      for (j=0;j<160;j++) 
        gsmsample[j]=((short)(Src[j+i*160]-128))<<8;
      gsm_encode(gsmhandle,gsmsample,gsmbuf);
      Dest[offset]=1;
      offset++;
      memcpy(Dest+offset,gsmbuf,33);
      rsize+=34;
      offset+=33;
    }
  }
  Dest[rsize]=0;
  MMstore(m,dest,0,rsize);
  MMset(m,0,(dest<<1)|1);
  return 0;
}
/* fun [S GsmCodec] S */
int AudioGsmDecrunch (mmachine m)
{
  int src,dest,codec,n,i,j,slen,offset=0,rsize=0;
  char *Src,*Dest;

  codec=MMpull(m)>>1;
  src=MMget(m,0)>>1;
  if (src==NIL) { MMset(m,0,NIL);return 0;}
  if (codec==NIL) { MMset(m,0,NIL);return 0;}
  gsmhandle=(gsm)MMfetch(m,codec,0);
  if (gsmhandle==NULL) { MMset(m,0,NIL);return 0;}

  slen=MMsizestr(m,src)-1;
  Src=MMstartstr(m,src);
  if (Src==NULL) { MMset(m,0,NIL);return 0;}
  
  n=0;
  while (offset<slen)
  {
    if (Src[offset++])
    {
      offset+=33;
    }
    n++;
  }

  dest=MMmalloc(m,n*(160>>2)+1+1,TYPEBUF);
  src=MMget(m,0)>>1;
  MMset(m,0,NIL);
  Src=MMstartstr(m,src);
  if (Src==NULL) return 0;
  if (dest==NIL) return 0;
  Dest=MMstartstr(m,dest);
  MMstore(m,dest,0,n*(160));

  i=0;
  offset=0;
  while (i<n)
   {
    if (Src[offset])
    {
      offset++;
      memcpy(gsmbuf,Src+offset,33);
      gsm_decode(gsmhandle,gsmbuf,gsmsample);
      for (j=0;j<160;j++) Dest[j+i*(160)]=(gsmsample[j]>>8)+128;
      offset+=33;
    }
    else
    {
      memset(&Dest[i*(160)],128,160);
      offset++;
    }
    i++;
  }
  Dest[n*160]=0;

  MMset(m,0,(dest<<1)|1);
  return 0;
}

/* 16 bits =======================================================*/
/* fun [S GsmCodec] S */
int AudioGsmDecrunch16 (mmachine m)
{
  int src,dest,codec,n,i,slen,offset=0,rsize=0;
  char *Src;
  short *Dest;

  codec=MMpull(m)>>1;
  src=MMget(m,0)>>1;
  if (src==NIL) { MMset(m,0,NIL);return 0;}
  if (codec==NIL) { MMset(m,0,NIL);return 0;}
  gsmhandle=(gsm)MMfetch(m,codec,0);
  if (gsmhandle==NULL) { MMset(m,0,NIL);return 0;}

  slen=MMsizestr(m,src);
  Src=MMstartstr(m,src);
  if (Src==NULL) { MMset(m,0,NIL);return 0;}
  
  n=0;
  while (offset<slen)
  {
    if (Src[offset++])
    {
      offset+=33;
    }
    n++;
  }

  dest=MMmalloc(m,n*(160>>1)+1+1,TYPEBUF);
  src=MMget(m,0)>>1;
  MMset(m,0,NIL);
  Src=MMstartstr(m,src);
  if (Src==NULL) return 0;
  if (dest==NIL) return 0;
  Dest=(short*)MMstartstr(m,dest);
  MMstore(m,dest,0,n*(160*2));

  i=0;
  offset=0;
  while (i<n)
  {
    if (Src[offset])
    {
      offset++;
      memcpy(gsmbuf,Src+offset,33);
      gsm_decode(gsmhandle,gsmbuf,gsmsample);
/*      for (j=0;j<160;j++) Dest[j+i*(160)]=(gsmsample[j]);*/
      memcpy (&Dest[i*160],gsmsample,320);
      offset+=33;
    }
    else
    {
      memset(&Dest[i*(160)],0,160*2);
      offset++;
    }
    i++;
  }
  ((char*)Dest)[n*160*2]=0;

  MMset(m,0,(dest<<1)|1);
  return 0;
}

int isemptybuf16(short *s,int level,int size)
{
  int i=0;
  int total=0;
  if (!size) 
  {
    return 1;
  }
  while (i<size) total+=abs(s[i++]);
  if (((total)/size)<level) return 1;
  return 0;
}

int AudioGsmSetNoiseLevel (mmachine m)
{
  int level;

  level=MMget(m,0)>>1;

  NoiseLevel=level;
  MMset(m,0,NoiseLevel<<1);
  return 0;
}

/* fun [S GsmCodec] S */
int AudioGsmCrunch16 (mmachine m)
{
  int src,dest,n,codec,i,slen,offset=0,rsize=0;
  short *Src;
  char *Dest;

  codec=MMpull(m)>>1;
  src=MMget(m,0)>>1;
  if (src==NIL) { MMset(m,0,NIL);return 0;}
  if (codec==NIL) { MMset(m,0,NIL);return 0;}
  slen=MMsizestr(m,src)/2;
  gsmhandle=(gsm)MMfetch(m,codec,0);
  if (gsmhandle==NULL) { MMset(m,0,NIL);return 0;}

//  if (slen%160) return 0;
  n=slen/160;
  dest=MMmalloc(m,((n*34)>>2)+1,TYPEBUF);
  src=MMget(m,0)>>1;
  Src=(short*)MMstartstr(m,src);
  if (Src==NULL) return 0;
  MMset(m,0,NIL);
  if (dest==NIL) return 0;
  Dest=MMstartstr(m,dest);
  for (i=0;i<n;i++)
  {
    if (isemptybuf16(&Src[i*160],NoiseLevel,160)) 
    {
      Dest[offset]=0;
      offset++;
      rsize++;
    }
    else
    {
      memcpy(gsmsample,&Src[i*160],160*2);
      gsm_encode(gsmhandle,gsmsample,gsmbuf);
      Dest[offset]=1;
      offset++;
      memcpy(Dest+offset,gsmbuf,33);
      rsize+=34;
      offset+=33;
    }
  }
  Dest[rsize]=0;
  MMstore(m,dest,0,rsize);
  MMset(m,0,(dest<<1)|1);
  return 0;
}

/* Noise Gate */ 
/* fun [S I I] S */
int AudioNoiseGate8 (mmachine m)
{
  int src,level,time,slen,i,j,tmp;
  char *Src;

  time=MMpull(m)>>1;
  level=MMpull(m)>>1;
  src=MMget(m,0)>>1;
  MMset(m,0,NIL);
  if (src==NIL) return 0;

  Src=MMstartstr(m,src);
  slen=MMsizestr(m,src);
  if (Src==NULL) return 0;

  i=0;
  while (i<slen)
  {
    if (abs(Src[i]-128)>level) tmp=time;
    else
    {
      tmp--;
      if (tmp==0)
      {
        for (j=0;j<time;j++) Src[i-j]=(char)128;
        while (abs(Src[i]-128)>level)
          Src[i++]=(char)128;
        tmp=time;
      }
    }
    i++;
  }
  MMset(m,0,(src<<1)|1);
  return 0;
}

int AudioGetLevel8 (mmachine m)
{
  int src,level,slen,i;
  char *Src;
  level=0;

  src=MMget(m,0)>>1;
  MMset(m,0,NIL);
  if (src==NIL) return 0;

  Src=MMstartstr(m,src);
  slen=MMsizestr(m,src);
  if (Src==NULL) return 0;

  i=0;
  while (i<slen)
  {
    level+=abs(Src[i]);
    i++;
  }
  MMset(m,0,(level/slen)<<1);
  return 0;
}

int AudioGetLevel16 (mmachine m)
{
  int src,level=0,slen,i;
  short *Src;

  src=MMget(m,0)>>1;
  MMset(m,0,NIL);
  if (src==NIL) return 0;

  Src=(short*)MMstartstr(m,src);
  slen=MMsizestr(m,src)/2;
  if (Src==NULL) return 0;

  i=0;
  while (i<slen)
  {
    level+=abs(Src[i]);
    i++;
  }
  MMset(m,0,(level/slen)<<1);
  return 0;
}

int AudioNoiseGate16 (mmachine m)
{
  int src,level,time,slen,i,j,tmp;
  short *Src;

  time=MMpull(m)>>1;
  level=MMpull(m)>>1;
  src=MMget(m,0)>>1;
  MMset(m,0,NIL);
  if (src==NIL) return 0;

  Src=(short*)MMstartstr(m,src);
  slen=MMsizestr(m,src)/2;
  if (Src==NULL) return 0;

  i=0;
  while (i<slen)
  {
    if (abs(Src[i]-32768)>level) tmp=time;
    else
    {
      tmp--;
      if (tmp==0)
      {
        for (j=0;j<time;j++) Src[i-j]=(short)32768;
        while (abs(Src[i]-32768)>level)
          Src[i++]=(short)32768;
        tmp=time;
      }
    }
    i++;
  }
  MMset(m,0,(src<<1)|1);
  return 0;
}

/* Normalyse */
int AudioNormalyse8 (mmachine m)
{
  int src,slen,i,max=0;
  char *Src;

  src=MMget(m,0)>>1;
  MMset(m,0,NIL);
  if (src==NIL) return 0;

  Src=MMstartstr(m,src);
  slen=MMsizestr(m,src);
  if (Src==NULL) return 0;

  for (i=0;i<slen;i++)
  {
    if (abs(Src[i]-128)>max) max=abs(Src[i]-128);
  }
  
  for (i=0;i<slen;i++)
  {
    if (abs(Src[i]-128)>max) Src[i]=(((Src[i]-128)*127)/max)+128;
  }

  MMset(m,0,(src<<1)|1);
  return 0;
}

int AudioNormalyse16 (mmachine m)
{
  int src,slen,i,max=0;
  short *Src;

  src=MMget(m,0)>>1;
  MMset(m,0,NIL);
  if (src==NIL) return 0;

  Src=(short*)MMstartstr(m,src);
  slen=MMsizestr(m,src)/2;
  if (Src==NULL) return 0;

  for (i=0;i<slen;i++)
  {
    if (abs(Src[i]-32768)>max) max=abs(Src[i]-32768);
  }
  
  for (i=0;i<slen;i++)
  {
    if (abs(Src[i]-32768)>max) Src[i]=(((long)(Src[i]-32768)*32767)/max)+32768;
  }

  MMset(m,0,(src<<1)|1);
  return 0;
}


