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

    FILE....: VPBVOX.CPP
    TYPE....: C++ Module
    AUTHOR..: David Rowe
    DATE....: 18/6/98

    This file contains the VPB API functions required to control the
    VOX firmware.


         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 <assert.h>
#include <cmath>

#include "vpbvox.h"
#include "apifunc.h"
#include "mapdev.h"
#include "objtrack.h"


// Vox mode switches
#define	VOX_SIMPLE		0
#define	VOX_VOLTAGE	      	1

// defaults -----------------------------------------------------------

#define	DEFAULT_MODE		0	// SIMPLE mode as default

// default VOX_SIMPLE params
#define DEFAULT_ONLEVEL		-40.0	// on level	
#define DEFAULT_OFFLEVEL	-50.0	// off level
#define	DEFAULT_RUNON		2000	// runon in ms

// default VOX_VOLTAGE params
#define DEFAULT_ONLEVEL_V	-18.0	// on level	
#define DEFAULT_OFFLEVEL_V	-24.0	// off level
#define	DEFAULT_RUNON_V		30000	// safety runon in ms
#define DEFAULT_TLEVEL_V	-2.0	// transient level	
#define	DEFAULT_TRUNON_V	2000	// transient runon in ms
#define	DEFAULT_WAIT_V		100	// delay in ms

// limits to params

#define	VOXMAX			-2.0	// -2 dB maximum
#define	VOXMIN			-100.0	// -100 dB minimum
#define	VOXRUNMAX		30000	// 30 sec max runon
#define	VOXRUNMIN		50	// 50 ms min runon
#define	VOXMAXLIN		32767	// 0 dB maximum
#define	RUNONMAX		65535   // max runon 65.5 seconds
#define	FS		      	8000	// sampling rate	

typedef struct {
	unsigned short	 id;		// object ID
	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   mode;		// vox algorithm mode
	unsigned short	 tlevel;
	unsigned short	 trunon;
	unsigned short   wait;
} VOXCONFIG;


static VOXCONFIG *voxconfig;	// ptr to array of VOX structures
				// stores vox config for each channel


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

	FUNCTION.: vpbvox_open()
	AUTHOR...: David Rowe
	DATE.....: 18/6/98

	Initialises the vox module.  This function should be called when the 
	API is initialised.  This function loads up the default VOX parameters
	for each channel.

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

void vpbvox_open(unsigned short numch) {
	int		i;
	word	m[PC_LVOX_UPDATE];
	unsigned short	b,ch;
	VPBREG	*v;

	voxconfig = new VOXCONFIG[numch];

	// config DSP state variables and mirror in PC

	m[0] = PC_LVOX_UPDATE;
	m[1] = PC_VOX_UPDATE;

	for(i=0; i<numch; i++) {
		maphndletodev(i, &b, &ch);
		v = vpb_c->vpbreg(b);

		switch( v->model )
		{
		    case VPB_V4LOG:
		    case VPB_V4PCI: break;
		    default:        continue;
		}

		voxconfig[i].mode = DEFAULT_MODE;

		if (voxconfig[i].mode == VOX_SIMPLE) {
			voxconfig[i].id = objtrack_handle_to_id(VOXOBJ, i);
			voxconfig[i].onlevel =
	                (unsigned short)(VOXMAXLIN*pow(10.0, DEFAULT_ONLEVEL/20.0));
			voxconfig[i].offlevel =
			(unsigned short)(VOXMAXLIN*pow(10.0, DEFAULT_OFFLEVEL/20.0));
			voxconfig[i].runon =
			(unsigned short)((float)DEFAULT_RUNON/1000)*(FS/v->lsf);
			// for completness
			voxconfig[i].tlevel = 0;
			voxconfig[i].trunon = 0;
			voxconfig[i].wait = 0;
		}
		else {
			voxconfig[i].id = objtrack_handle_to_id(VOXOBJ, i);
			voxconfig[i].onlevel =
			(unsigned short)(VOXMAXLIN*pow(10.0,DEFAULT_ONLEVEL_V/20.0));
			voxconfig[i].offlevel = (unsigned short)
			(VOXMAXLIN*pow(10.0, DEFAULT_OFFLEVEL_V/20.0));
			voxconfig[i].runon = (unsigned short)
			(((float)DEFAULT_RUNON_V/1000)*(FS/v->lsf));

			voxconfig[i].tlevel = (unsigned short)
			(VOXMAXLIN*pow(10.0, DEFAULT_TLEVEL_V/20.0));
			voxconfig[i].trunon = (unsigned short)
			(((float)DEFAULT_TRUNON_V/1000)*(FS/v->lsf));
			voxconfig[i].wait = (unsigned short)
			(((float)DEFAULT_WAIT_V/1000)*(FS/v->lsf));
		}

		m[2] = voxconfig[i].id;
		m[3] = voxconfig[i].onlevel;
		m[4] = voxconfig[i].offlevel;
		m[5] = voxconfig[i].runon;
		m[6] = voxconfig[i].mode;
		m[7] = voxconfig[i].tlevel;
		m[8] = voxconfig[i].trunon;
		m[9] = voxconfig[i].wait;
		vpb_c->PutMessageVPB(b,m);
	}
}

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

	FUNCTION.: vpbvox_close()
	AUTHOR...: David Rowe
	DATE.....: 19/6/98

	Closes down vpbvox module.

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

void vpbvox_close() {
	delete [] voxconfig;
}

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

	FUNCTION.: vpb_setvox()
	AUTHOR...: David Rowe
	DATE.....: 16/6/98

	Sets the vox parameters for a specified channel.

	int    handle  handle of channel device
	VOX    *vox    VOX parameters 

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

int WINAPI vpb_setvox(int handle, VPB_VOX *vox)
{
	unsigned short b;      // board number
	unsigned short ch;     // channel on board b
	word   m[PC_LVOX_UPDATE];
	VPBREG *v;
	unsigned short runon_frames;
	int    frames_sec;
	float  runon_secs;

	// validate

	ValidHandleCheck(handle);
	if (vox->onlevel > VOXMAX) assert(0);
	if (vox->onlevel < VOXMIN) assert(0);
	if (vox->offlevel > VOXMAX) assert(0);
	if (vox->offlevel < VOXMIN) assert(0);
	if (vox->runon > VOXRUNMAX) assert(0);
	if (vox->runon < VOXRUNMIN) assert(0);
	maphndletodev(handle, &b, &ch);
	v = vpb_c->vpbreg(b);
	runon_secs = vox->runon/(float)1000.0;
	frames_sec =  FS/v->lsf;
	runon_frames = (unsigned short)(runon_secs*frames_sec);

//	if (runon_frames > RUNONMAX) assert(0);


	m[0] = PC_LVOX_UPDATE;
	m[1] = PC_VOX_UPDATE;
	switch( v->model )
	{
	    case VPB_V4LOG:
	    case VPB_V4PCI:
		// update PC mirror of vox config
		voxconfig[handle].onlevel = (unsigned short)(VOXMAXLIN*pow(10.0, vox->onlevel/20.0));
		voxconfig[handle].offlevel = (unsigned short)(VOXMAXLIN*pow(10.0, vox->offlevel/20.0));
		voxconfig[handle].runon = runon_frames;
		m[2] = voxconfig[handle].id;
		m[3] = voxconfig[handle].onlevel;
		m[4] = voxconfig[handle].offlevel;
		m[5] = voxconfig[handle].runon;
		break;

	    default:
		m[2] = ch;
		m[3] = (unsigned short)(VOXMAXLIN*pow(10.0, vox->onlevel/20.0));
		m[4] = (unsigned short)(VOXMAXLIN*pow(10.0, vox->offlevel/20.0));
		m[5] = runon_frames;
	}

	// update DSPs vox params

	m[6] = VOX_SIMPLE;
	vpb_c->PutMessageVPB(b,m);

	return VPB_OK;
}

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

	FUNCTION.: vpb_getvox()
	AUTHOR...: David Rowe
	DATE.....: 16/6/98

	Gets the vox parameters for a specified channel.

	int    handle   handle of channel device
	VOX    *vox	ptr to returned VOX parameters 

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

int WINAPI vpb_getvox(int handle, VPB_VOX *vox)
{
	VPBREG *v;
	unsigned short b,ch;
	long   runon;

	ValidHandleCheck(handle);
	maphndletodev(handle, &b, &ch);
	v = vpb_c->vpbreg(b);

	if (v->model == VPB_V4LOG || v->model == VPB_V4PCI){
		vox->onlevel = (float) 20.0*(float)log10((float)voxconfig[handle].onlevel/VOXMAXLIN);
		vox->offlevel = (float)20.0 * (float)log10((float)voxconfig[handle].offlevel/VOXMAXLIN);
		runon = (long) ((float)voxconfig[handle].runon * 1000.0) / (FS/v->lsf);
		vox->runon = (unsigned short)runon;
	} else {
		vox->onlevel  = 0.0;
		vox->offlevel = 0.0;
		vox->runon    = 0;
	}
	return VPB_OK;
}

