/*
 *	clipcntm.c
 *
 *	汎用カウンタ管理
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 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は割込処理から呼ばれる可能性を考慮して排他制御の必要があるが、汎用カウンタ管理は(その目的から考えて)割込処理から呼ばれることは無く排他制御は必要無い。
 *	* Wed Feb 04 21:22:53 JST 2015 Naoyuki Sawa
 *	- Hewは要素数[0]の配列に対応していないので、要素数[1]として調整しました。
 *	  P/ECE開発環境(GCC)やWindows(VC++6.0)は、要素数[0]の配列に対応しているので、関係有りません。
 *	* Thu Feb 04 21:41:31 JST 2016 Naoyuki Sawa
 *	- 値の範囲を、(0〜UINT_MAX)⇒(INT_MIN〜INT_MAX)に変更しました。
 *	  これまでは、メモリ節約のために(0〜UINT_MAX)としていたのですが、実用上この範囲では足りない事が判ったので、(INT_MIN〜INT_MAX)に変更する事にしました。
 *	  今回の変更によって、メモリ使用量が2倍になりました。
 *	  しかし、極端に多くのチャネルを使うようなアプリケーションでなければ、大きな問題にはならないと思います。
 *	- 関数名を、旧:CntMgr_Init()⇒新:CntMgr_New()、旧:CntMgr_InitStatic()⇒新:CntMgr_Init() に変更しました。
 *	  最近作成している他のモジュールの関数名に合わせるためです。
 *	  今後は、まだ古い名前のままになっている他の古いモジュールの関数も、上記の関数名に合わせて行く予定です。
 *	* Wed Mar 02 21:54:20 JST 2016 Naoyuki Sawa
 *	- '前の変化が終わる前に、次の変化を開始した時'の動作を変更しました。
 *	  変更前は、前の変化の途中の現在値から、新しい目標値へ、新しい変化速度で変化を開始していました。
 *	  変更後は、前の変化を即完了させて、前の変化の目標値(即完了した結果の値)から、新しい目標値へ、新しい変化速度で変化を開始するようにしました。
 *	  要するに、前の変化が完了しているか未完了であるかに関係無く、新しい変化の動作は同じになります。
 *	- これまで、'前の変化が終わる前に、次の変化を開始した時'に、前の変化の途中の現在値からスムーズに繋がるようにしていたのは、意図的なものだったのですが、
 *	  しばらく使って見た結果、見た目にそのメリットがわかる事はほとんど無かった事と、それよりもデメリットの方が大きい事が判ったので、変更する事にしました。
 *	  変更前の動作で、問題が生じたのは、以下のようなケースです。
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,   0, 0,0);	//即、0にする。
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,1000,10,1);	//0⇒1000に向けて、毎フレーム10づつ変化を開始する。
 *	  │	〜〜〜 この間の時間の長短が、ユーザーの操作タイミングに依存すると仮定する 〜〜〜
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,1010,1,1);	//カウンタが既に1000に到達しているつもりで、1000⇒1010へ向けて、1づつ変化を開始したつもり。
 *	  │							//しかし例えば、ユーザーが素早く操作すると、カウンタの現在値がまだ100あたりの可能性も有る。
 *	  │							//変更前の動作では、カウンタの現在値から、新しい目標値へ、新しい変化速度で変化を開始していた。
 *	  │							//つまり、100⇒1010へ向けて、1づつ変化を開始する事になり、完了までに非常に時間が掛かる事になる。	←←←問題点
 *	- 上記の問題を避けるために、前の変化を確実に完了させてから、新しい変化を開始させる事にしました。
 *	  前の変化を即完了させるために、大きな変化が生じる事も有るのですが、この点が気になる事はほとんど無いと思います。(TAPミキサーも同様の挙動にしています。)
 *	  それよりも、タイミングに依存した動作の不安定さが無くなる事のメリットの方が大きいと思います。
 *	  変更後の動作では、上述の例の操作を行うと、以下の動作になります。
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,   0, 0,0);	//即、0にする。
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,1000,10,1);	//0⇒1000に向けて、毎フレーム10づつ変化を開始する。
 *	  │	〜〜〜 この間の時間の長短が、ユーザーの操作タイミングに依存すると仮定する 〜〜〜
 *	  │	CntMgr_SetVal(pCntMgr,iCntMgrCh,1010,1,1);	//カウンタの現在値に関係なく、前の変化が即完了して1000になる。
 *	  │							//その後、1000から1010へ向けて、1づつ変化を開始する。
 *	* Wed Oct 19 21:15:15 JST 2016 Naoyuki Sawa
 *	- 汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
 *	- CntMgr_GetErrTerm()を追加しました。
 */
#include "clip.h"
/****************************************************************************
 *	ローカル関数
 ****************************************************************************/
//{{2016/10/19追加:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
//汎用カウンタ管理チャネル構造体を取得する。
static ST_CntMgrCh* CntMgr_GetCntMgrCh(ST_CntMgr* pCntMgr, int iCntMgrCh) {
	if((unsigned)iCntMgrCh >= (unsigned)pCntMgr->nCntMgrCh) { DIE(); }	//チャネル番号が範囲外
	return &pCntMgr->TBL_CntMgrCh[iCntMgrCh];
}
//}}2016/10/19追加:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/
ST_CntMgr* CntMgr_New(int nCntMgrCh) {
	ST_CntMgr* pCntMgr;
	//汎用カウンタ管理構造体のメモリを確保する。
#ifndef __RENESAS__
	pCntMgr = malloc(sizeof(ST_CntMgr) + (sizeof(ST_CntMgrCh) *  nCntMgrCh));
#else //__RENESAS__
	pCntMgr = malloc(sizeof(ST_CntMgr) + (sizeof(ST_CntMgrCh) * (nCntMgrCh - 1/*ST_CntMgr.pCntMgrCh[0]の分*/)));		//Hewは要素数[0]の配列に対応していないので、要素数[1]として調整しました。
#endif//__RENESAS__
	if(!pCntMgr) { DIE(); }	//メモリ不足
	//汎用カウンタ管理構造体を初期化する。
	CntMgr_Init(pCntMgr, nCntMgrCh);
	return pCntMgr;
}
/*--------------------------------------------------------------------------*/
void CntMgr_Init(ST_CntMgr* pCntMgr, int nCntMgrCh) {
	//汎用カウンタ管理構造体をクリアする。
#ifndef __RENESAS__
	memset(pCntMgr, 0, sizeof(ST_CntMgr) + (sizeof(ST_CntMgrCh) *  nCntMgrCh));
#else //__RENESAS__
	memset(pCntMgr, 0, sizeof(ST_CntMgr) + (sizeof(ST_CntMgrCh) * (nCntMgrCh - 1/*ST_CntMgr.pCntMgrCh[0]の分*/)));		//Hewは要素数[0]の配列に対応していないので、要素数[1]として調整しました。
#endif//__RENESAS__
	//汎用カウンタ管理構造体に、汎用カウンタ管理チャネル数を格納する。
	pCntMgr->nCntMgrCh = nCntMgrCh;
}
/*--------------------------------------------------------------------------*/
void CntMgr_Exec(ST_CntMgr* pCntMgr) {
	int iCntMgrCh;
	for(iCntMgrCh = 0; iCntMgrCh < pCntMgr->nCntMgrCh; iCntMgrCh++) {
		//汎用カウンタ管理チャネル構造体を取得する。
//{{2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
//		ST_CntMgrCh* pCntMgrCh = &pCntMgr->TBL_CntMgrCh[iCntMgrCh];
//↓2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
		ST_CntMgrCh* pCntMgrCh = CntMgr_GetCntMgrCh(pCntMgr, iCntMgrCh);
//}}2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
		//現在値と目標値を取り出しておく。
		int ValNow = pCntMgrCh->ValNow;	//現在値
		int ValTo  = pCntMgrCh->ValTo;	//目標値
		//現在値と目標値が違うならば(=変化中ならば)…
		if(ValNow != ValTo) {
			//誤差項に変化速度を加算し、今回変化量を求める。
			unsigned ErrTerm = pCntMgrCh->ErrTerm + pCntMgrCh->Rate;	//pCntMgrCh->ErrTerm=0〜(INT_MAX  -1) ＋ pCntMgrCh->Rate =1〜INT_MAX ⇒ ErrTerm=1〜(INT_MAX*2-1)
			unsigned quot    = ErrTerm / (unsigned)pCntMgrCh->Scale;	//ErrTerm           =1〜(INT_MAX*2-1) ÷ pCntMgrCh->Scale=1〜INT_MAX ⇒ quot   =0〜(INT_MAX*2-1)
			unsigned rem     = ErrTerm % (unsigned)pCntMgrCh->Scale;	//ErrTerm           =1〜(INT_MAX*2-1) ％ pCntMgrCh->Scale=1〜INT_MAX ⇒ rem    =0〜(INT_MAX  -1)
			//今回変化量を除いた残りを、誤差項に書き戻す。
			pCntMgrCh->ErrTerm = rem;					//pCntMgrCh->ErrTerm=0〜(INT_MAX  -1)
			//変化方向が増加の場合…														//┐
			if(ValNow < ValTo) {															//│
				unsigned diff = ValTo - ValNow;		//0〜UINT_MAX	//残り変化量を求める。							//│
				ValNow += (quot < diff) ? quot : diff;			//今回変化量,又は,残り変化量のうち、小さい方で現在値を増加する。	//│
			//変化方向が減少の場合…														//├もしValToにINT_MIN,又は,INT_MAX付近の値が設定されても、この方法ならばオーバーフローせずに正しく処理出来ます。
			} else {																//│
				unsigned diff = ValNow - ValTo;		//0〜UINT_MAX	//残り変化量を求める。							//│
				ValNow -= (quot < diff) ? quot : diff;			//今回変化量,又は,残り変化量のうち、小さい方で現在値を減少する。	//│
			}																	//┘
			//現在値を書き戻す。
			pCntMgrCh->ValNow = ValNow;
		}
	}
}
/*--------------------------------------------------------------------------*/
void CntMgr_SetVal(ST_CntMgr* pCntMgr, int iCntMgrCh, int ValTo, int Rate, int Scale) {
	ST_CntMgrCh* pCntMgrCh;
//{{2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
//	if(((unsigned)iCntMgrCh >= (unsigned)pCntMgr->nCntMgrCh) || (Rate < 0) || (Scale < 0)) { DIE(); }	//引数が不正
//	//汎用カウンタ管理チャネル構造体を取得する。
//	pCntMgrCh = &pCntMgr->TBL_CntMgrCh[iCntMgrCh];
//↓2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
	if((Rate < 0) || (Scale < 0)) { DIE(); }	//引数が不正
	//汎用カウンタ管理チャネル構造体を取得する。
	pCntMgrCh = CntMgr_GetCntMgrCh(pCntMgr, iCntMgrCh);
//}}2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
//{{2016/03/02変更:'前の変化が終わる前に、次の変化を開始した時'の動作を変更しました。
//	//パラメータが一個でも違ったら、新しい変化を開始する。パラメータが同じならば、現在の変化を継続する。
//	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; }
//	}
//↓2016/03/02変更:'前の変化が終わる前に、次の変化を開始した時'の動作を変更しました。
	//前の変化の変化中ならば、即完了させる。
	// - 前の変化が既に完了していたら、既に(ValNow==ValTo)になっているから、この処理はダミー処理となる。
	pCntMgrCh->ValNow  = pCntMgrCh->ValTo;	//現在値
	//新しい変化のパラメータを格納する。
	pCntMgrCh->ValTo   = ValTo;		//目標値
	pCntMgrCh->Rate    = Rate;		//変化速度
	pCntMgrCh->Scale   = Scale;		//変化速度
	pCntMgrCh->ErrTerm = 0;			//誤差項
	//変化速度のパラメータが片方でも0(=即反映)ならば、目標値を現在値へ即反映する。
	if(!Rate || !Scale) { pCntMgrCh->ValNow = ValTo; }
//}}2016/03/02変更:'前の変化が終わる前に、次の変化を開始した時'の動作を変更しました。
}
/*--------------------------------------------------------------------------*/
int CntMgr_GetVal(ST_CntMgr* pCntMgr, int iCntMgrCh) {
	ST_CntMgrCh* pCntMgrCh;
//{{2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
//	if((unsigned)iCntMgrCh >= (unsigned)pCntMgr->nCntMgrCh) { DIE(); }	//引数が不正
//	//汎用カウンタ管理チャネル構造体を取得する。
//	pCntMgrCh = &pCntMgr->TBL_CntMgrCh[iCntMgrCh];
//↓2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
	//汎用カウンタ管理チャネル構造体を取得する。
	pCntMgrCh = CntMgr_GetCntMgrCh(pCntMgr, iCntMgrCh);
//}}2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
	//現在値を返す。
	return pCntMgrCh->ValNow;
}
/*--------------------------------------------------------------------------*/
void CntMgr_GetValAll(ST_CntMgr* pCntMgr, int ValNow[/*nCntMgrCh*/]) {
	int iCntMgrCh;
	//各汎用カウンタ管理チャネルについて…
	for(iCntMgrCh = 0; iCntMgrCh < pCntMgr->nCntMgrCh; iCntMgrCh++) {
		//汎用カウンタ管理チャネル構造体を取得する。
//{{2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
//		ST_CntMgrCh* pCntMgrCh = &pCntMgr->TBL_CntMgrCh[iCntMgrCh];
//↓2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
		ST_CntMgrCh* pCntMgrCh = CntMgr_GetCntMgrCh(pCntMgr, iCntMgrCh);
//}}2016/10/19変更:汎用カウンタ管理チャネル構造体を取得する処理を、ローカル関数として共通化しました。
		//現在値を取得する。
		ValNow[iCntMgrCh] = pCntMgrCh->ValNow;
	}
}
/*--------------------------------------------------------------------------*/
//{{2016/10/19追加:CntMgr_GetErrTerm()を追加しました。
double CntMgr_GetErrTerm(ST_CntMgr* pCntMgr, int iCntMgrCh) {
	double dErrTerm = 0.0;	//変化中でなかった場合のために、戻り値を0.0としておく。	//0.0
	//汎用カウンタ管理チャネル構造体を取得する。
	ST_CntMgrCh* pCntMgrCh = CntMgr_GetCntMgrCh(pCntMgr, iCntMgrCh);
	//変化中ならば…
	if(pCntMgrCh->ValNow != pCntMgrCh->ValTo) {
		dErrTerm = (double)pCntMgrCh->ErrTerm / (double)pCntMgrCh->Scale;	//0.0⇒ (1.0-ε)
		//変化方向が減少ならば…
		if(pCntMgrCh->ValNow > pCntMgrCh->ValTo) {
			//戻り値をマイナスにする。
			dErrTerm = -dErrTerm;						//0.0⇒-(1.0-ε)
		}
	}
	return dErrTerm;
}
//}}2016/10/19追加:CntMgr_GetErrTerm()を追加しました。
