/*---------------------------------------------------------------------------*\

	FILE....: hostvox.cpp
	TYPE....: C File
	AUTHOR..: Voicetronix
	DATE....: 13/02/2006

	HostDSP VOX detection.


         Voicetronix Voice Processing Board (VPB) Software
         Copyright (C) 1999-2006 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License as published by the Free Software Foundation; either
         version 2.1 of the License, or (at your option) any later version.

         This library 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
         Lesser General Public License for more details.

         You should have received a copy of the GNU Lesser General Public
         License along with this library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#include "hostvox.h"
#include "mess.h"

#include <cstdlib>


#define IDLE 0
#define BUSY 1
#define VOX_FRAME (8*20)
/* structure to hold data for each vox detector */
struct voxstate {
	unsigned short	id;		/* corresponds to channel number              */
	unsigned short	state;		/* current state of the vox detector          */
	unsigned short	onlevel;	/* signal has to exceed this level            */
	unsigned short	offlevel;	/* countdown starts when signal drops below   */
	unsigned short	runon;		/* how many frames to count down              */
	unsigned short	count;		/* frames before detector reports off         */
	long		py;		/* vox energy filter state variable */
	short		dcoffset;
	short		voxbuf[VOX_FRAME];	/* Buffer if we are not passed a VOX_FRAME worth of data */
	int		bufcount;
};

void hostvox_open(void **vsv, int id)
{
	voxstate *vs;
	*vsv = new voxstate;
	vs = (voxstate*)*vsv;
	if (vs != NULL){
		vs->id = id;
		vs->state = IDLE;
		vs->onlevel = 8000;
		vs->offlevel = 4000;
		vs->runon = 500;
		vs->count = 0;
		vs->py = 0;
		vs->dcoffset=0;
		vs->bufcount=0;
		mprintf("hostvox_open: Opened!\n");
	} else {
		mprintf("hostvox_open: Failed!\n");
		*vsv = NULL;
	}
}

void hostvox_close(void *vsv)
{
	voxstate *vs = (voxstate*)vsv;
	delete vs;
	mprintf("hostvox_close: Closed!\n");
}

void hostvox_update_levels(void *vsv, word *m)
{
	voxstate *v = (voxstate*)vsv;

	if(v != NULL){
		mprintf("hostvox_update_levels: old onlevel [%d]\n",v->onlevel);
		v->onlevel  = m[3];
		mprintf("hostvox_update_levels: new onlevel [%d]\n",v->onlevel);
		mprintf("hostvox_update_levels: old offlevel [%d]\n",v->offlevel);
		v->offlevel = m[4];
		mprintf("hostvox_update_levels: new offlevel [%d]\n",v->offlevel);
		mprintf("hostvox_update_levels: old runon [%d]\n",v->runon);
		v->runon    = m[5];
		mprintf("hostvox_update_levels: new runon [%d]\n",v->runon);
	}
}

int hostvox_analyse(void *vsv, short *samples, int n)
{
	voxstate *v = (voxstate*)vsv;
	int i;
	short xmax;
	short yshort;
	long acc, y, dcacc=0;
	int next_state;
	int ret=0;
	short *buf;
	int count=0;

	if (v == NULL){
		return 0;
	}
	if (n < VOX_FRAME){
		for(i=0;(v->bufcount<VOX_FRAME)&&(i<n);i++){
			v->voxbuf[v->bufcount] = samples[i];
			v->bufcount++;
		}
		if (v->bufcount < VOX_FRAME){
			return 0;
		}
		else {
			buf = v->voxbuf;
			v->bufcount=0;
			count = VOX_FRAME;
		}
	}
	else {
		buf = samples;
		count = n;
	}

	// retrieve saved energy filter state
	y = v->py;

	/* Energy Filter frame and find max */
	xmax =0;
	for (i=0;i<count;i++){
		acc = y >> 16;
		y -= acc << 6;
		y += ((long)abs(buf[i] - v->dcoffset)) << 5;

		yshort = y >> 15;
		if (yshort>xmax)
			xmax = yshort;
		dcacc += buf[i];
	}
	v->dcoffset = (short)((v->dcoffset+dcacc)/(count+1));
	//mprintf("xmax %d (%d <> %d) offset %d energy %ld\n", xmax,v->onlevel, v->offlevel, v->dcoffset, y);

	// save energy filter state
	v->py = y;

	next_state = v->state;
	switch(v->state){
	case IDLE:
		if (xmax > v->onlevel){
			v->count = v->runon;
			next_state = BUSY;
			ret = 2;
		}
		break;
	case BUSY:
		if (xmax < v->offlevel){
			v->count--;
			if(v->count == 0){
				next_state = IDLE;
				ret = 1;
			}
		}
		else {
			v->count = v->runon;
		}
		break;
	}
	v->state = next_state;
	return ret;
}

long hostvox_energy(void *vsv)
{
	voxstate *v = (voxstate*)vsv;

	return v->py;
}
