/*
 *	clipcntm.c
 *
 *	汎用カウンタ管理
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Sun Apr 13 21:02:48 JST 2014 Naoyuki Sawa
 *	- 1st リリース。
 *	- Sound Mixer(clipsndx.*)を元にして作成しました。
 *	  Sound Mixer(clipsndx.*)と処理内容が似ていますが、下記の相違のために共通化することは難しく、別機能として分けることにしました。
 *	  ・Sound Mixerは8ビット値であるが、汎用カウンタ管理は8ビットでは足りない。
 *	  ・Sound Mixerは各チャネルのサウンドチャネルマスクが必要だが、汎用カウンタ管理には必要無い。
 *	  ・Sound Mixerは各チャネルの減衰量の合計を求める必要があるが、汎用カウンタ管理には必要無い。
 *	  ・Sound Mixerは割込処理から呼ばれる可能性を考慮して排他制御の必要があるが、汎用カウンタ管理は(その目的から考えて)割込処理から呼ばれることは無く排他制御は必要無い。
 */
#include "clip.h"

/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/

ST_CntMgr* CntMgr_Init(int nCntMgrCh) {
	ST_CntMgr* pCntMgr;
	/* 汎用カウンタ管理構造体のメモリを確保する。 */
	pCntMgr = malloc(sizeof(ST_CntMgr) + (sizeof(ST_CntMgrCh) * nCntMgrCh));
	if(!pCntMgr) { DIE(); } /* メモリ不足 */
	/* 汎用カウンタ管理構造体を初期化する。 */
	CntMgr_InitStatic(pCntMgr, nCntMgrCh);
	return pCntMgr;
}

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

void CntMgr_InitStatic(ST_CntMgr* pCntMgr, int nCntMgrCh) {
	/* 汎用カウンタ管理構造体をクリアする。 */
	memset(pCntMgr, 0, sizeof(ST_CntMgr) + (sizeof(ST_CntMgrCh) * nCntMgrCh));
	/* 汎用カウンタ管理構造体に、汎用カウンタ管理チャネル数を格納する。 */
	pCntMgr->nCntMgrCh = nCntMgrCh;
}

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

void CntMgr_Exec(ST_CntMgr* pCntMgr) {
	int iCntMgrCh;
	for(iCntMgrCh = 0; iCntMgrCh < pCntMgr->nCntMgrCh; iCntMgrCh++) {
		/* 汎用カウンタ管理チャネル構造体を取得する。 */
		ST_CntMgrCh* pCntMgrCh = &pCntMgr->pCntMgrCh[iCntMgrCh];
		/* 現在値と目標値を取り出しておく。 */
		int ValNow = pCntMgrCh->ValNow;	/* 現在値 */
		int ValTo  = pCntMgrCh->ValTo;	/* 目標値 */
		/* 現在値と目標値が違うならば(=変化中ならば)… */
		if(ValNow != ValTo) {
			/* 誤差項に変化速度を加算し、今回変化量を求める。 */
			div_t d = div(pCntMgrCh->ErrTerm + pCntMgrCh->Rate, pCntMgrCh->Scale);
			/* 今回変化量を除いた残りを、誤差項に書き戻す。 */
			pCntMgrCh->ErrTerm = d.rem;
			/* 今回変化量を、現在値に反映する。 */
			if(ValNow < ValTo) { if((ValNow += d.quot) > ValTo) { ValNow = ValTo; } } /* 変化方向が増加の場合 */
			else               { if((ValNow -= d.quot) < ValTo) { ValNow = ValTo; } } /* 変化方向が減少の場合 */
			/* 現在値を書き戻す。 */
			pCntMgrCh->ValNow = ValNow;
		}
	}
}

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

void CntMgr_SetVal(ST_CntMgr* pCntMgr, int iCntMgrCh, int ValTo, int Rate, int Scale) {
	ST_CntMgrCh* pCntMgrCh;
	if(((unsigned)iCntMgrCh >= pCntMgr->nCntMgrCh) || ((unsigned)ValTo > USHRT_MAX) || ((unsigned)Rate > USHRT_MAX) || ((unsigned)Scale > USHRT_MAX)) { DIE(); } /* 引数不正 */
	/* 汎用カウンタ管理チャネル構造体を取得する。 */
	pCntMgrCh = &pCntMgr->pCntMgrCh[iCntMgrCh];
	/* パラメータが一個でも違ったら、新しい変化を開始する。パラメータが同じならば、現在の変化を継続する。 */
	if((pCntMgrCh->ValTo != ValTo) || (pCntMgrCh->Rate != Rate) || (pCntMgrCh->Scale != Scale)) {
		/* パラメータを格納する。 */
		pCntMgrCh->ValTo   = ValTo;	/* 目標値 */
		pCntMgrCh->Rate    = Rate;	/* 変化速度 */
		pCntMgrCh->Scale   = Scale;	/* 変化速度 */
		pCntMgrCh->ErrTerm = 0;		/* 誤差項 */
		/* 変化速度のパラメータが片方でも0(=即反映)ならば、目標値を現在値へ即反映する。 */
		if(!Rate || !Scale) { pCntMgrCh->ValNow = ValTo; }
	}
}

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

int CntMgr_GetVal(ST_CntMgr* pCntMgr, int iCntMgrCh) {
	ST_CntMgrCh* pCntMgrCh;
	if((unsigned)iCntMgrCh >= pCntMgr->nCntMgrCh) { DIE(); } /* 引数不正 */
	/* 汎用カウンタ管理チャネル構造体を取得する。 */
	pCntMgrCh = &pCntMgr->pCntMgrCh[iCntMgrCh];
	/* 現在値を返す。 */
	return pCntMgrCh->ValNow;
}

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

void CntMgr_GetValAll(ST_CntMgr* pCntMgr, uint16_t ValNow[/*nCntMgrCh*/]) {
	int iCntMgrCh;
	/* 各汎用カウンタ管理チャネルについて… */
	for(iCntMgrCh = 0; iCntMgrCh < pCntMgr->nCntMgrCh; iCntMgrCh++) {
		/* 汎用カウンタ管理チャネル構造体を取得する。 */
		ST_CntMgrCh* pCntMgrCh = &pCntMgr->pCntMgrCh[iCntMgrCh];
		/* 現在値を格納する。 */
		ValNow[iCntMgrCh] = pCntMgrCh->ValNow;
	}
}

