/*****************************************************************************/

/*
 *      audio.c  --  Audio processing for "virtual transceiver".
 *
 *      Copyright (C) 1999  Thomas Sailer (sailer@ife.ee.ethz.ch)
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Please note that the GPL allows you to use the driver, NOT the radio.
 *  In order to use the radio, you need a license from the communications
 *  authority of your country.
 *
 */

/*****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <math.h>

#include "eppfm.h"

/* ---------------------------------------------------------------------- */

#define PWRINTERVAL     4096         /* must be even */
#define PWRINTERVAL2      64         /* must be even */
#define PWRNOISECALIB   4096

/* ---------------------------------------------------------------------- */

struct {
	/* samling rates */
	unsigned int srateepp, sratedsp;
	/* sampling rate converter */
	unsigned int ph, phinc;
	/* power meter */
	unsigned int pwrcnt;
	unsigned int pwracc;
	/* freqency generators */
	unsigned int ph0, ph1, freq0, freq1;
} istate;

struct {
	/* samling rates */
	unsigned int srateepp, sratedsp;
	/* sampling rate converter */
	unsigned int ph, phinc;
	/* power meter */
	unsigned int pwrcnt, pwrcnt2;
	unsigned int pwracc1;
	int pwracc2, pwracc3;
	float pwracc4;
	/* carrier detect state */
	unsigned int dcdparam, dcd;
} ostate;

/* ---------------------------------------------------------------------- */

static const int16_t rxfilter[16][16] = {
	{ 139,  -57, -451,  612,  1263, -2887, -2021, 13678, 24449, 12513, -2539, -2588, 1360,  498, -450, -31 },
	{ 138,  -85, -445,  728,  1141, -3169, -1432, 14824, 24343, 11337, -2984, -2276, 1432,  385, -443,  -6 },
	{ 137, -115, -432,  842,   993, -3427,  -772, 15942, 24131, 10160, -3358, -1956, 1480,  277, -430,  16 },
	{ 134, -146, -410,  954,   820, -3657,   -42, 17022, 23817,  8989, -3661, -1632, 1504,  173, -412,  36 },
	{ 130, -177, -380, 1061,   623, -3854,   753, 18058, 23401,  7832, -3895, -1309, 1507,   76, -390,  54 },
	{ 124, -210, -341, 1161,   402, -4012,  1613, 19040, 22888,  6698, -4062,  -991, 1490,  -15, -365,  71 },
	{ 117, -243, -294, 1253,   159, -4127,  2531, 19961, 22282,  5594, -4165,  -682, 1454,  -97, -337,  85 },
	{ 108, -275, -237, 1333,  -105, -4194,  3505, 20813, 21589,  4528, -4208,  -386, 1401, -172, -307,  97 },
	{  97, -307, -172, 1401,  -386, -4208,  4528, 21589, 20813,  3505, -4194,  -105, 1333, -237, -275, 108 },
	{  85, -337,  -97, 1454,  -682, -4165,  5594, 22282, 19961,  2531, -4127,   159, 1253, -294, -243, 117 },
	{  71, -365,  -15, 1490,  -991, -4062,  6698, 22888, 19040,  1613, -4012,   402, 1161, -341, -210, 124 },
	{  54, -390,   76, 1507, -1309, -3895,  7832, 23401, 18058,   753, -3854,   623, 1061, -380, -177, 130 },
	{  36, -412,  173, 1504, -1632, -3661,  8989, 23817, 17022,   -42, -3657,   820,  954, -410, -146, 134 },
	{  16, -430,  277, 1480, -1956, -3358, 10160, 24131, 15942,  -772, -3427,   993,  842, -432, -115, 137 },
	{  -6, -443,  385, 1432, -2276, -2984, 11337, 24343, 14824, -1432, -3169,  1141,  728, -445,  -85, 138 },
	{ -31, -450,  498, 1360, -2588, -2539, 12513, 24449, 13678, -2021, -2887,  1263,  612, -451,  -57, 139 }
};

#define RXFPH(x)  (((x)>>(PHASEFRAC-4))&15)
#define RXFLEN    16

/* ---------------------------------------------------------------------- */

void audio_1750(int on)
{
	if (on && istate.srateepp > 0)
		istate.freq0 = (1750*65536 + istate.srateepp/2) / istate.srateepp;
	else
		istate.freq0 = 0;
	istate.freq1 = 0;
	istate.ph0 = 0;
	istate.ph1 = 0;
}

/*
 *
 * DTMF frequencies
 *
 *      1209 1336 1477 1633
 *  697   1    2    3    A
 *  770   4    5    6    B
 *  852   7    8    9    C
 *  941   *    0    #    D
 * 
 */

void audio_dtmf(int ch)
{
	static const unsigned char dtmftransl[0x10] = {
		0x13, 0x00, 0x10, 0x20, 0x01, 0x11, 0x21, 0x02,
		0x12, 0x22, 0x30, 0x31, 0x32, 0x33, 0x03, 0x23
	};
	static const unsigned int freqlogrp[4] = { 697, 770, 852, 941 };
	static const unsigned int freqhigrp[4] = { 1209, 1336, 1477, 1633 };
	unsigned int tr;

	if (ch & 16 && istate.sratedsp > 0) {
		tr = dtmftransl[ch & 15];
		istate.freq0 = (freqlogrp[tr & 15]*65536+istate.srateepp/2)/istate.srateepp;
		istate.freq1 = (freqhigrp[tr >> 4]*65536+istate.srateepp/2)/istate.srateepp;
	} else {
		istate.freq0 = 0;
		istate.freq1 = 0;
	}	
	istate.ph0 = 0;
	istate.ph1 = 0;
}

/* ---------------------------------------------------------------------- */

void audio_setsquelch(int sq)
{
	ostate.dcdparam = !!sq;
}

int audio_getsquelch(void)
{
	return ostate.dcd;
}

/* ---------------------------------------------------------------------- */

extern inline int calcfilter(const int16_t *coeff, const int8_t *data)
{
	unsigned cnt;
	int sum = 0;
	
	for (cnt = 0; cnt < RXFLEN; cnt++)
		sum += (*coeff++) * (*data--);
	return sum >> 8;
}

/* ---------------------------------------------------------------------- */

void audio_initinput(unsigned int srateepp, unsigned int sratedsp)
{
	memset(&istate, 0, sizeof(istate));
	istate.srateepp = srateepp;
	istate.sratedsp = sratedsp;
	istate.phinc = ((1 << PHASEFRAC) * sratedsp + srateepp / 2) / srateepp;
	lprintf(2, "rec phase inc: 0x%05x\n", istate.phinc);
}

unsigned int audio_convertinput(unsigned int num, const int16_t *ibuf, signed char *obuf)
{
	unsigned int cnt = 0;
	unsigned int i;
	float pw;
	int s;
	
	for (i = 0; i < num; i++) {
		istate.pwracc += ibuf[i] * ibuf[i];
		if ((++istate.pwrcnt) >= PWRINTERVAL) {
			pw = istate.pwracc;
			istate.pwracc = 0;
			istate.pwrcnt = 0;
			pw *= (1.0 / PWRINTERVAL);
			if (pw < 1.6384)
				pw = -40;
			else
				pw = (10.0 / M_LN10) * log(pw) - 42.144;
			gui_updatevu(pw, -40);
		}
	}
	istate.ph &= PHASEMASK;
	while (istate.ph < (num << PHASEFRAC)) {
		s = ibuf[istate.ph >> PHASEFRAC] >> 8;
		s += SIN(istate.ph0) >> 9;
		s += SIN(istate.ph1) >> 9;
		if (s > 127)
			s = 127;
		if (s < -128)
			s = -128;
		obuf[cnt] = s;
		istate.ph += istate.phinc;
		cnt++;
		istate.ph0 += istate.freq0;
		istate.ph1 += istate.freq1;
	}
	gui_addsamples(num, ibuf);
	return cnt;
}

int audio_adjustinput(int eppcnt, int dspcnt, int eppdelay)
{
	lprintf(4, "eppcnt %d dspcnt %d eppdelay %d\n", eppcnt, dspcnt, eppdelay);
	if (eppdelay > SNDLATENCY) {
		istate.phinc++;
		istate.ph += PHASEMASK >> 2;
		if (istate.ph > PHASEMASK)
			istate.ph = PHASEMASK;
	} else if (eppdelay < SNDLATENCY) {
		istate.phinc--;
		istate.ph -= PHASEMASK >> 2;
		if (istate.ph > PHASEMASK)
			istate.ph = 0;
	}
	if (istate.phinc < (0x9 << (PHASEFRAC-4)) || istate.phinc > (0x1f << (PHASEFRAC-4))) {
		lprintf(0, "phinc (0x%05x) out of range\n", istate.phinc);
		return -1;
	}
	lprintf(3, "phase increment: 0x%05x\n", istate.phinc);
	return 0;
}

void audio_initoutput(unsigned int srateepp, unsigned int sratedsp)
{
	memset(&ostate, 0, sizeof(ostate));
	ostate.srateepp = srateepp;
	ostate.sratedsp = sratedsp;
	ostate.phinc = ((1 << PHASEFRAC) * srateepp + sratedsp / 2) / sratedsp;
	lprintf(2, "play phase inc: 0x%05x\n", ostate.phinc);
}

unsigned int audio_convertoutput(unsigned int num, const signed char *ibuf, int16_t *obuf)
{
	unsigned int cnt = 0;
	unsigned int i;
	float pw1, pw2;
	
	for (i = 0; i < num; i++) {
		ostate.pwracc1 += ibuf[i] * ibuf[i];
		switch (ostate.pwrcnt2 & 3) {
		case 0:
			ostate.pwracc2 -= ibuf[i];
			break;

		case 1:
			ostate.pwracc3 -= ibuf[i];
			break;

		case 2:
			ostate.pwracc2 += ibuf[i];
			break;

		default:
			ostate.pwracc3 += ibuf[i];
			break;
		}
		if ((++ostate.pwrcnt2) >= PWRINTERVAL2) {
			ostate.pwracc4 += ostate.pwracc2 * ostate.pwracc2 + ostate.pwracc3 * ostate.pwracc3;
			ostate.pwracc2 = 0;
			ostate.pwracc3 = 0;
			ostate.pwrcnt2 = 0;			
		}
		if ((++ostate.pwrcnt) >= PWRINTERVAL) {
			pw1 = ostate.pwracc1;
			pw2 = ostate.pwracc4;
			ostate.pwracc1 = 0;
			ostate.pwracc4 = 0;
			ostate.pwrcnt = 0;
			pw1 *= (1.0 / PWRINTERVAL);
			pw2 *= (1.0 / PWRINTERVAL / PWRINTERVAL) * PWRNOISECALIB;
			if (pw1 < 1.6384)
				pw1 = -40;
			else
				pw1 = (10.0 / M_LN10) * log(pw1) - 42.144;
			if (pw2 < 1.6384)
				pw2 = -40;
			else
				pw2 = (10.0 / M_LN10) * log(pw2) - 42.144;
			if (pw2 > 0)
				pw2 = 0;
			gui_updatevu(pw1, pw2);
			ostate.dcd = (pw1 > -30) && (pw2 < (pw1 / 2 - 20));
		}
	}
	ostate.ph &= PHASEMASK;
	while (ostate.ph < (num << PHASEFRAC)) {
#if 0
		obuf[cnt] = ibuf[ostate.ph >> PHASEFRAC] << 8;
#else
		obuf[cnt] = calcfilter(rxfilter[RXFPH(ostate.ph)], ibuf+(ostate.ph >> PHASEFRAC));
#endif
		ostate.ph += ostate.phinc;
		cnt++;
	}
	gui_addsamples(cnt, obuf);
	if (!ostate.dcd && ostate.dcdparam)
		memset(obuf, 0, 2*cnt);
	return cnt;
}

int audio_adjustoutput(int dspcnt, int eppcnt, int dspdelay)
{
	lprintf(4, "dspcnt %d eppcnt %d dspdelay %d\n", dspcnt, eppcnt, dspdelay);
	ostate.ph &= PHASEMASK;
	if (dspdelay > SNDLATENCY) {
		ostate.phinc++;
		ostate.ph += PHASEMASK >> 2;
		if (ostate.ph > PHASEMASK)
			ostate.ph = PHASEMASK;
	} else if (dspdelay < SNDLATENCY) {
		ostate.phinc--;
		ostate.ph -= PHASEMASK >> 2;
		if (ostate.ph > PHASEMASK)
			ostate.ph = 0;
	} if (ostate.phinc < (0x9 << (PHASEFRAC-4)) || ostate.phinc > (0x1f << (PHASEFRAC-4))) {
		lprintf(0, "phinc (0x%05x) out of range\n", ostate.phinc);
		return -1;
	}
	lprintf(3, "phase increment: 0x%05x\n", ostate.phinc);
	return 0;
}

/* ---------------------------------------------------------------------- */
