/*
 *	clipadp.c
 *
 *	2-bit ADPCM Decoder
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2017 Naoyuki Sawa
 *
 *	* Wed Dec 20 00:13:10 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	* Wed Dec 20 17:31:02 JST 2006 Naoyuki Sawa
 *	- 1st リリース。
 *	* Thu Mar 01 01:53:43 JST 2007 Naoyuki Sawa
 *	- adpcm_stat()を追加しました。
 *	* Mon Jul 15 20:32:11 JST 2013 Naoyuki Sawa
 *	- ADPCMCHANNEL構造体を並び替え、volフィールドを追加しました。
 *	- 音量設定機能に対応するため、adpcm_att()関数を追加しました。
 *	- 高速RAMを節約するため、係数テーブルをframadp.cからclipadp.cへ移動しました。
 *	* Mon Jun 26 22:31:39 JST 2017 Naoyuki Sawa
 *	- adpcm_play()にて、volatile指定が不足していたが偶然動作していたコードを、きちんとvolatileを付けるように修正しました。
 *	- adpcm_rate()関数を追加しました。
 *	* Tue Jun 27 22:55:03 JST 2017 Naoyuki Sawa
 *	- 2017/06/26の変更によって最終ループの停止直前の1サンプルが出力されなくなっていた問題を修正しました。
 *	- ループ時もprogressをリセットせずに引き継ぐ事にしました。
 *	* Wed Aug 16 22:59:27 JST 2017 Naoyuki Sawa
 *	- 64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
 *	* Sat Aug 19 22:58:59 JST 2017 Naoyuki Sawa
 *	- adpcm_play()においてlenの計算がオーバーフローして、間違った長さで再生してしまう事が有ったバグを修正しました。
 */
#include "clip.h"

/****************************************************************************
 *	ローカル変数
 ****************************************************************************/

static ADPCMDRIVER driver;

//{{2013/07/15追加:高速RAMを節約するため、係数テーブルをframadp.cからclipadp.cへ移動しました。
//static const short adpcm_tbl[4/*ADPCM値*/][2] = {
//	{  128, 233 },	/* + 0,4 */
//	{  384, 375 },	/* + 4,4 */
//	{ -128, 233 },	/* + 8,4 */
//	{ -384, 375 },	/* +12,4 */
//};			/* =16   */
//↓* Fri Dec 22 00:19:41 JST 2006 Naoyuki Sawa
#define A	233	/* =(    1.1^-1)*256 */
#define B	375	/* =(    1.1^ 4)*256 */
#define C	141	/* =(0.5/1.1^-1)*256 */
#define D	263	/* =(1.5/1.1^ 4)*256 */
/*static*/ const short adpcm_tbl[4/*ADPCM値*/][2] = {
#ifndef ADPCM_ASM
	{ A,  C },
	{ B,  D },
	{ A, -C },
	{ B, -D },
#else /*ADPCM_ASM*/
	/* ASM版では、ワード単位でいちどに取得して、下位ハーフ、上位ハーフの順に利用します。
	 * 上位ハーフを下位ハーフへ移動する際、"xsra %rd,16"では2命令に展開されますが、
	 * 以下のように、予め上位ハーフをSwapしておけば、"swap %rd,%rs"1命令で移動できます。
	 */
	{ A, SWAPH( C) },
	{ B, SWAPH( D) },
	{ A, SWAPH(-C) },
	{ B, SWAPH(-D) },
#endif /*ADPCM_ASM*/
};
#undef A
#undef B
#undef C
#undef D
//}}2013/07/15追加:高速RAMを節約するため、係数テーブルをframadp.cからclipadp.cへ移動しました。

/****************************************************************************
 *	ローカル関数
 ****************************************************************************/

static int
//{{2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
//adpcm_stream_callback(short wbuff[/*ADPCMBUFLEN*/], int param/*NOUSE*/)
//↓2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
adpcm_stream_callback(short wbuff[/*ADPCMBUFLEN*/], intptr_t param/*NOUSE*/)
//}}2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
{
	static int mixbuf[ADPCMBUFLEN]; /* ミキシングバッファ、ＳＴＡＴＩＣです！ */
	//
	int i;

	/* ミキシングバッファをクリアします。 */
	memset(mixbuf, 0, sizeof mixbuf);

	/* 各チャネルについて... */
	for(i = 0; i < driver.nch; i++) {
		/* 再生中ならば... */
		if(driver.pch[i].loop) {
			/* チャネルの出力を、ミキシングバッファへ合成します。 */
			adpcm_mix1(&driver.pch[i], mixbuf);
		}
	}

	/* ミキシングバッファをクリッピングして、出力バッファへ書き込みます。 */
	adpcm_mix2(wbuff, mixbuf);

	return 0;
}

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

void
adpcm_init(int nch)
{
	int i;

	/* 確実に未初期化状態にします。 */
	adpcm_exit();

	/* チャネル構造体を確保します。 */
	driver.nch = nch;
	driver.pch = calloc(nch, sizeof(ADPCMCHANNEL));
	if(!driver.pch) {
		DIE(); /* メモリ不足 */
	}

//{{2013/07/15追加
	/* 全チャネルの音量を、減衰無しに初期化します。 */
	for(i = 0; i < driver.nch; i++) {
		adpcm_att(i, 0);
	}
//}}2013/07/15追加

	/* ストリーム再生を開始します。
	 * - スタックの使用頻度が少ないので、スタック切替を使わないことにします。
	 *   実際のアプリケーションで試してみて、どちらが速いか判断する予定です。
	 * ↓2006/12/21追記
	 * - 予測どおり、スタック切替を使わない方が高速であることを確認しました。
	 */
	stream_play(ADPCMBUFLEN, adpcm_stream_callback, 0/*NOUSE*/, 0/*スタック切換使わない*/);
}

void
adpcm_exit()
{
	/* 確実にストリーム再生を停止します。 */
	stream_stop();

	/* 確実にチャネル構造体を解放します。 */
	free(driver.pch/*NULLOK*/);
	memset(&driver, 0, sizeof driver); /*必要!!*/
}

void
adpcm_play(int ch, const void* data, int loop)
{
//{{2017/06/26変更:adpcm_play()にて、volatile指定が不足していたが偶然動作していたコードを、きちんとvolatileを付けるように修正しました。
//	ADPCMCHANNEL* pch;
//↓2017/06/26変更:adpcm_play()にて、volatile指定が不足していたが偶然動作していたコードを、きちんとvolatileを付けるように修正しました。
// * Mon Jun 26 22:31:39 JST 2017 Naoyuki Sawa
// - この下の処理で、pch->loopに値を二回格納する順序に依存しているので、pchにはvolatile指定が必須でした。
//   これまではpchにvolatileを指定しておらず、厳密に言えば、コンパイラが一回目の「pch->loop = 0;」を最適化して削除したり、pch->loopとその他のフィールドの格納順序を入れ替える可能性も有り、もしそうなると正しく動作しなかったのですが、
//   たまたま、gcc33.exeが、pchにvolatileを指定した場合と同じ動作をするコードを生成していたようで、偶然、正しく動作していました。
//   とは言え、pchにvolatile指定が不足したままだと、今後、細かなコードの変更によってGCCの最適化が変わり、正しく動作しなくなるコードが生成される恐れが有るので、今回、きちんと、pchにvolatile指定を追加しておく事にしました。
	volatile ADPCMCHANNEL* pch;
//}}2017/06/26変更:adpcm_play()にて、volatile指定が不足していたが偶然動作していたコードを、きちんとvolatileを付けるように修正しました。
	const PCEWAVEINFO* pwi;

	if((ch < 0) || (ch > driver.nch - 1)) {
		DIE(); /* 未初期化、または、チャネル番号不正 */
	}
	pch = &driver.pch[ch];

	if(memcmp(data, "MCPP", 4) != 0) {
		DIE(); /* シグネチャが不正 */
	}
	pwi = (const PCEWAVEINFO*)((const unsigned char*)data + 4/*シグネチャ*/ + 4/*サイズ*/);
	if((pwi->type & 3) != PW_TYPE_2BITADPCM) {
		DIE(); /* 2bit-ADPCMでない */
	}

//{{2017/06/26変更:adpcm_rate()関数を追加しました。
//	pch->loop = 0;
//	/*{{この間で割り込みが発生しても、停止チャネルと見なされるので安全です*/
////{{2013/07/15変更
////	pch->pcm      = 0;
////	pch->data     = 0;
////	pch->delta    = 16;
////	pch->progress = 0;
////	pch->rate     = pwi->resv;
////	pch->len      = pwi->len;
////	pch->ptr      = (const unsigned char*)(pwi + 1); /* "pwi->pData"ではありません!! */
////	pch->init_len = pch->len;
////	pch->init_ptr = pch->ptr;
////↓2013/07/15変更
//	pch->ptr      = (const unsigned char*)(pwi + 1); /* "pwi->pData"ではありません!! */
//	pch->len      = pwi->len;
//	pch->pcm      = 0;
//	pch->data     = 0;
//	pch->delta    = 16;
//	pch->progress = 0;
//	pch->rate     = pwi->resv;
////	pch->vol      = 再生開始時に音量を初期化せず、設定されている音量を引き継ぐことにしました。pceWaveSetChAtt()の挙動に合わせるためです。
//	pch->init_ptr = pch->ptr;
//	pch->init_len = pch->len;
////}}2013/07/15変更
//	/*}}この間で割り込みが発生しても、停止チャネルと見なされるので安全です*/
//	pch->loop = loop;
//↓2017/06/26変更:adpcm_rate()関数を追加しました。
	{
		int rate = pwi->resv;
	//{{2018/08/19変更:adpcm_play()においてlenの計算がオーバーフローして、間違った長さで再生してしまう事が有ったバグを修正しました。
	//	int len  = ((pwi->len * rate) + (SPEAKER_FREQUENCY - 1)) / SPEAKER_FREQUENCY;	//16000Hz換算でのサンプル数⇒実サンプル数に変換する。'/clip/tool/dppcmcnv/main.cのwriteadpcm2()'にて実サンプル数⇒16000Hz換算でのサンプル数に変換する時は端数を切り捨てているので、ここでは逆に端数を切り上げる事によって元の実サンプル数に戻せるはず。(※要検証)
	//↓2018/08/19変更:adpcm_play()においてlenの計算がオーバーフローして、間違った長さで再生してしまう事が有ったバグを修正しました。
		int len  = (((int64_t)pwi->len * rate) + (SPEAKER_FREQUENCY - 1)) / SPEAKER_FREQUENCY;	//16000Hz換算でのサンプル数⇒実サンプル数に変換する。'/clip/tool/dppcmcnv/main.cのwriteadpcm2()'にて実サンプル数⇒16000Hz換算でのサンプル数に変換する時は端数を切り捨てているので、ここでは逆に端数を切り上げる事によって元の実サンプル数に戻せるはず。(※要検証)
		//           ~~~~~~~~~例えばサウンドデータが16KHzで約17秒以上の時に、(pwi->len*rate)がオーバーフローして式全体が間違った結果になる。正しい結果を得るためには式全体を64bitで計算する必要が有り、そのためにはpwi->len,又は,rateのどちらか一方(両方でも構わないが)を64bitにキャストすれば良い。今回はpwi->lenを64bitにキャストする方法で対応した。
	//}}2018/08/19変更:adpcm_play()においてlenの計算がオーバーフローして、間違った長さで再生してしまう事が有ったバグを修正しました。
		const unsigned char* ptr = (const unsigned char*)(pwi + 1); /* "pwi->pData"ではありません!! */
		{
			pch->loop = 0;
			/*{{この間で割り込みが発生しても、停止チャネルと見なされるので安全です*/
			pch->ptr      = ptr;
			pch->len      = len;
			pch->pcm      = 0;
			pch->data     = 0;
			pch->delta    = 16;
			pch->progress = 0;
			pch->rate     = rate;
		//	pch->vol      = 再生開始時に音量を初期化せず、設定されている音量を引き継ぐことにしました。pceWaveSetChAtt()の挙動に合わせるためです。
			pch->init_ptr = ptr;
			pch->init_len = len;
			/*}}この間で割り込みが発生しても、停止チャネルと見なされるので安全です*/
			pch->loop = loop;
		}
	}
//}}2017/06/26変更:adpcm_rate()関数を追加しました。
}

void
adpcm_stop(int ch)
{
	ADPCMCHANNEL* pch;

	if((ch < 0) || (ch > driver.nch - 1)) {
		DIE(); /* 未初期化、または、チャネル番号不正 */
	}
	pch = &driver.pch[ch];

	pch->loop = 0; /* 1ワードの書き込みだけなので、割り込み禁止は不要です */
}

int
adpcm_stat(int ch)
{
	ADPCMCHANNEL* pch;

	if((ch < 0) || (ch > driver.nch - 1)) {
		DIE(); /* 未初期化、または、チャネル番号不正 */
	}
	pch = &driver.pch[ch];

	return pch->loop; /* 1ワードの読み出しだけなので、割り込み禁止は不要です */
}

//{{2013/07/15追加
void
adpcm_att(int ch, int att)
{
	static const unsigned char dbtbl[64] = {
		256/*(1<<8)/(10^( 0/20))*/-1,229/*(1<<8)/(10^( 1/20))*/-1,204/*(1<<8)/(10^( 2/20))*/-1,182/*(1<<8)/(10^( 3/20))*/-1,
		162/*(1<<8)/(10^( 4/20))*/-1,144/*(1<<8)/(10^( 5/20))*/-1,129/*(1<<8)/(10^( 6/20))*/-1,115/*(1<<8)/(10^( 7/20))*/-1,
		102/*(1<<8)/(10^( 8/20))*/-1, 91/*(1<<8)/(10^( 9/20))*/-1, 81/*(1<<8)/(10^(10/20))*/-1, 73/*(1<<8)/(10^(11/20))*/-1,
		 65/*(1<<8)/(10^(12/20))*/-1, 58/*(1<<8)/(10^(13/20))*/-1, 52/*(1<<8)/(10^(14/20))*/-1, 46/*(1<<8)/(10^(15/20))*/-1,
		 41/*(1<<8)/(10^(16/20))*/-1, 37/*(1<<8)/(10^(17/20))*/-1, 33/*(1<<8)/(10^(18/20))*/-1, 29/*(1<<8)/(10^(19/20))*/-1,
		 26/*(1<<8)/(10^(20/20))*/-1, 23/*(1<<8)/(10^(21/20))*/-1, 21/*(1<<8)/(10^(22/20))*/-1, 19/*(1<<8)/(10^(23/20))*/-1,
		 17/*(1<<8)/(10^(24/20))*/-1, 15/*(1<<8)/(10^(25/20))*/-1, 13/*(1<<8)/(10^(26/20))*/-1, 12/*(1<<8)/(10^(27/20))*/-1,
		 11/*(1<<8)/(10^(28/20))*/-1, 10/*(1<<8)/(10^(29/20))*/-1,  9/*(1<<8)/(10^(30/20))*/-1,  8/*(1<<8)/(10^(31/20))*/-1,
		  7/*(1<<8)/(10^(32/20))*/-1,  6/*(1<<8)/(10^(33/20))*/-1,  6/*(1<<8)/(10^(34/20))*/-1,  5/*(1<<8)/(10^(35/20))*/-1,
		  5/*(1<<8)/(10^(36/20))*/-1,  4/*(1<<8)/(10^(37/20))*/-1,  4/*(1<<8)/(10^(38/20))*/-1,  3/*(1<<8)/(10^(39/20))*/-1,
		  3/*(1<<8)/(10^(40/20))*/-1,  3/*(1<<8)/(10^(41/20))*/-1,  3/*(1<<8)/(10^(42/20))*/-1,  2/*(1<<8)/(10^(43/20))*/-1,
		  2/*(1<<8)/(10^(44/20))*/-1,  2/*(1<<8)/(10^(45/20))*/-1,  2/*(1<<8)/(10^(46/20))*/-1,  2/*(1<<8)/(10^(47/20))*/-1,
		  2/*(1<<8)/(10^(48/20))*/-1,  1/*(1<<8)/(10^(49/20))*/-1,  1/*(1<<8)/(10^(50/20))*/-1,  1/*(1<<8)/(10^(51/20))*/-1,
		  1/*(1<<8)/(10^(52/20))*/-1,  1/*(1<<8)/(10^(53/20))*/-1,  1/*(1<<8)/(10^(54/20))*/-1,  1/*(1<<8)/(10^(55/20))*/-1,
		  1/*(1<<8)/(10^(56/20))*/-1,  1/*(1<<8)/(10^(57/20))*/-1,  1/*(1<<8)/(10^(58/20))*/-1,  1/*(1<<8)/(10^(59/20))*/-1,
		  1/*(1<<8)/(10^(60/20))*/-1,  1/*(1<<8)/(10^(61/20))*/-1,  1/*(1<<8)/(10^(62/20))*/-1,  1/*(1<<8)/(10^(63/20))*/-1,
	};
	ADPCMCHANNEL* pch;

	if((ch < 0) || (ch > driver.nch - 1)) {
		DIE(); /* 未初期化、または、チャネル番号不正 */
	}
	pch = &driver.pch[ch];

	pch->vol = ((unsigned)att < 64) ? ((dbtbl[att]+1)<<1) : 0; /* 1ワードの書き込みだけなので、割り込み禁止は不要です */
	//      dbtbl[]を1バイト配列にするための補正分~~ ~~~スピーカー音量増幅のため、正確な音量よりも2倍の音量にした
}
//}}2013/07/15追加

//{{2017/06/26追加:adpcm_rate()関数を追加しました。
void adpcm_rate(int ch, int rate) {
	ADPCMCHANNEL* pch;
	if(((unsigned)ch >= (unsigned)driver.nch) ||		//未初期化、または、チャネル番号不正
	   ((unsigned)rate > SPEAKER_FREQUENCY)) { DIE(); }	//再生レートが不正
	pch = &driver.pch[ch];
	pch->rate = rate;	//1ワードの書き込みだけなので、割り込み禁止は不要です。
}
//}}2017/06/26追加:adpcm_rate()関数を追加しました。
