/*  
 *  This file is part of abc2ps, 
 *  Copyright (C) 1996-2007  Michael Methfessel
 *  See file abc2ps.c for details.
 */

/*  The contents of this file (sox.h) permit the generation of .au 
 *  files on arbitrary machines. All this material was taken from 
 *  playabc, Copyright (C) 1994  Don Ward.
 */


/**************************************************************************/
/*
 * These are a few defines which allow Don Ward's playabc and tune programs
 * to use sound_tools routines from the publicly available sox libst.a library.
 * This means that playabc and tune can be run on platforms other than Suns.
 * This should work fine with sox versions 7 and later.
 *
 * Observers will note that this file contains elements which were inspired
 * by both the Sun multimedia headers and the sox headers.
 * Steve Allen (sla@lick.ucsc.edu) can be blamed for this.
 *
 */

#define audio_s2u st_linear_to_ulaw

#define audio_s2d(pcms) (((unsigned short)(pcms)) == 0x8000 ? -1. : \
                        ((double)((short)(pcms))) / 32767.)

#define audio_d2s(pcmd) ((pcmd) >= 1. ? 32767 : (pcmd) <= -1. ? -32767 :\
                                    (short)((pcmd) * 32767.))

#define AUDIO_SUCCESS (0)
#define AUDIO_UNIXERROR (-1)

#define SUN_UNSPEC ((unsigned)(~0))        /* Unspecified data size */
#define SUN_MAGIC       0x2e736e64              /* Really '.snd' */
#define SUN_ULAW        1                       /* u-law encoding */
#define SUN_LIN_8       2                       /* Linear 8 bits */
#define SUN_LIN_16      3                       /* Linear 16 bits */
#define SUN_LIN_24      4                       /* Linear 24 bits */
#define SUN_LIN_32      5                       /* Linear 32 bits */
#define SUN_HDRSIZE     24                      /* Size of minimal header */


/* libst.c - portable sound tools library
*/

/*
** This routine converts from linear to ulaw.
**
** Craig Reese: IDA/Supercomputing Research Center
** Joe Campbell: Department of Defense
** 29 September 1989
**
** References:
** 1) CCITT Recommendation G.711  (very difficult to follow)
** 2) "A New Digital Technique for Implementation of Any
**     Continuous PCM Companding Law," Villeret, Michel,
**     et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
**     1973, pg. 11.12-11.17
** 3) MIL-STD-188-113,"Interoperability and Performance Standards
**     for Analog-to_Digital Conversion Techniques,"
**     17 February 1987
**
** Input: Signed 16 bit linear sample
** Output: 8 bit ulaw sample
*/

#define ZEROTRAP    /* turn on the trap as per the MIL-STD */
#define BIAS 0x84   /* define the add-in bias for 16 bit samples */
#define CLIP 32635

unsigned char
st_linear_to_ulaw( sample )
int sample;
    {
    static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
                               4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
                               5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
                               5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
                               6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
                               6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
                               6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
                               6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
    int sign, exponent, mantissa;
    unsigned char ulawbyte;

    /* Get the sample into sign-magnitude. */
    sign = (sample >> 8) & 0x80;		/* set aside the sign */
    if ( sign != 0 ) sample = -sample;		/* get magnitude */
    if ( sample > CLIP ) sample = CLIP;		/* clip the magnitude */

    /* Convert from 16 bit linear to ulaw. */
    sample = sample + BIAS;
    exponent = exp_lut[( sample >> 7 ) & 0xFF];
    mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
    ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
#ifdef ZEROTRAP
    if ( ulawbyte == 0 ) ulawbyte = 0x02;	/* optional CCITT trap */
#endif

    return ulawbyte;
    }

/*
** This routine converts from ulaw to 16 bit linear.
**
** Craig Reese: IDA/Supercomputing Research Center
** 29 September 1989
**
** References:
** 1) CCITT Recommendation G.711  (very difficult to follow)
** 2) MIL-STD-188-113,"Interoperability and Performance Standards
**     for Analog-to_Digital Conversion Techniques,"
**     17 February 1987
**
** Input: 8 bit ulaw sample
** Output: signed 16 bit linear sample
*/

int
st_ulaw_to_linear_slow( ulawbyte )
unsigned char ulawbyte;
    {
    static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
    int sign, exponent, mantissa, sample;

    ulawbyte = ~ ulawbyte;
    sign = ( ulawbyte & 0x80 );
    exponent = ( ulawbyte >> 4 ) & 0x07;
    mantissa = ulawbyte & 0x0F;
    sample = exp_lut[exponent] + ( mantissa << ( exponent + 3 ) );
    if ( sign != 0 ) sample = -sample;

    return sample;
    }


/*
 * Write out an unsigned long making sure that the byte order is big-endian
 * as is used by 680x0 and Sparc processors (which is where the use of
 * .snd and .au files originated--on Sun and NeXT systems).
 * This is to ensure that the byte order of the sound header is right.
 */
void be4(unsigned char * p, unsigned long ul)
{
  *p++ = (ul >> 24) & 0xff;
  *p++ = (ul >> 16) & 0xff;
  *p++ = (ul >> 8) & 0xff;
  *p++ = (ul) & 0xff;
}

/* --- write_header --- */
/* write a generic header for .au or .snd file as on Sun, NeXT systems */
int write_header (FILE *fpnt, int samples_per_sec, int bps, int chans)
{
  unsigned char buf[SUN_HDRSIZE];
  
  be4(&buf[0], SUN_MAGIC);
  be4(&buf[4], SUN_HDRSIZE);
  be4(&buf[8], SUN_UNSPEC);
  if (bps == 8) 
    be4(&buf[12], SUN_ULAW);
  else
    be4(&buf[12], SUN_LIN_16);
  be4(&buf[16], samples_per_sec);
  be4(&buf[20], chans);  
  /* and we leave the comment field as blank */

  if(fwrite(buf, 1, SUN_HDRSIZE, fpnt) == SUN_HDRSIZE)
    return AUDIO_SUCCESS;
  else
    return AUDIO_UNIXERROR;

}

/* --- auwrite --- */
void auwrite (FILE *fpnt, float *buf, int len, int bps, int chans) 
{
  int i;
  short w;
  char *b;
  unsigned char sample;
  
  if ((bps == 8) && (chans == 1)) {
    for (i=0; i<len; i++) {
      sample = audio_s2u(audio_d2s(buf[i]));;
      putc (sample, fpnt);
    }
    return;
  }
  
  if ((bps == 8) && (chans == 2)) {
    for (i=0; i<len; i++) {
      sample = audio_s2u(audio_d2s(buf[i]));;
      putc (sample, fpnt);
      putc (sample, fpnt);
    }
    return;
  }
  
  if ((bps == 16) && (chans == 1)) {
    b = (char *)&w;
    for (i=0; i<len; i++) {
      w = 32700 * buf[i];   
#if LIL_ENDIAN!=0
      fputc (*(b+1), fpnt);
      fputc (*b,     fpnt);
#else 
      fputc (*b,     fpnt); 
      fputc (*(b+1), fpnt);
#endif
    }
    return;
  }

  if ((bps == 16) && (chans == 2)) {
    b = (char *)&w;
    for (i=0; i<len; i++) {
      w = 32700 * buf[i];   
#if LIL_ENDIAN!=0
      fputc (*(b+1), fpnt);
      fputc (*b,     fpnt);
      fputc (*(b+1), fpnt);
      fputc (*b,     fpnt);
#else 
      fputc (*b,     fpnt); 
      fputc (*(b+1), fpnt);
      fputc (*b,     fpnt); 
      fputc (*(b+1), fpnt);
#endif
    }
    return;
  }

  printf ("+++ auwrite: cannot handle bps=%d chans=%d\n", bps, chans);
}

