/*
 * iaxclient: a cross-platform IAX softphone library
 *
 * Copyrights:
 * Copyright (C) 2003-2006, Horizon Wimba, Inc.
 * Copyright (C) 2007, Wimba, Inc.
 *
 * Contributors:
 * Steve Kann <stevek@stevek.com>
 *
 * This program is free software, distributed under the terms of
 * the GNU Lesser (Library) General Public License.
 */

#include "codec_ulaw.h"
#include "iaxclient_lib.h"

struct state {
    plc_state_t plc;
};

static short ulaw_2lin [256];
static unsigned char lin_2ulaw [16384];
static int initialized=0;

/* this looks similar to asterisk, but comes from public domain code by craig reese
   I've just followed asterisk's table sizes for lin_2u, and also too lazy to do binary arith to decide which
   iterations to skip -- this way we get the same result.. */
static void initialize() {
    int i;

    /* ulaw_2lin */
    for(i=0;i<256;i++) {
	  int b = ~i;
	  int exp_lut[8] = {0,132,396,924,1980,4092,8316,16764};
	  int sign, exponent, mantissa, sample;

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

    /* lin_2ulaw */
    for(i=-32767;i<32768;i+=4) {
	int sample = i;
	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 > 32635) sample = 32635;		/* clip the magnitude */

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

	lin_2ulaw[((unsigned short)i) >> 2] = ulawbyte;
    }

    initialized = 1;
}

static void destroy ( struct iaxc_audio_codec *c) {
	if ( c->decstate )
		free(c->decstate);
	free(c);
}


static int decode ( struct iaxc_audio_codec *c,
    int *inlen, unsigned char *in, int *outlen, short *out ) {
    struct state *state = (struct state *)c->decstate;
    short *orig_out = out;
    short sample;

    if(*inlen == 0) {
	int interp_len = 160;
	if(*outlen < interp_len) interp_len = *outlen;
	plc_fillin(&state->plc,out,interp_len);
	*outlen -= interp_len;
	return 0;
    }

    while ((*inlen > 0) && (*outlen > 0)) {
	sample = ulaw_2lin[(unsigned char)*(in++)];
	*(out++) = sample;
	(*inlen)--; (*outlen)--;
    }
    plc_rx(&state->plc, orig_out, (int)(out - orig_out));

    return 0;
}

static int encode ( struct iaxc_audio_codec *c,
    int *inlen, short *in, int *outlen, unsigned char *out ) {

    while ((*inlen > 0) && (*outlen > 0)) {
	*(out++) = lin_2ulaw[((unsigned short)*(in++)) >> 2];
	(*inlen)--; (*outlen)--;
    }

    return 0;
}


struct iaxc_audio_codec *codec_audio_ulaw_new() {

  struct iaxc_audio_codec *c = (struct iaxc_audio_codec *)calloc(sizeof(struct iaxc_audio_codec),1);

  if(!c) return c;

  if(!initialized) initialize();

  strcpy(c->name,"ulaw");
  c->format = IAXC_FORMAT_ULAW;
  c->encode = encode;
  c->decode = decode;
  c->destroy = destroy;

  /* really, we can use less, but don't want to */
  c->minimum_frame_size = 160;

  /* decoder state, used for interpolation */
  c->decstate = calloc(sizeof(struct state),1);
  plc_init(&((struct state *)c->decstate)->plc);

  return c;
}