/*
 *	clipleds.c
 *
 *	LEDシーケンサ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Sat Aug 31 17:23:06 JST 2013 Naoyuki Sawa
 *	- 1st リリース。
 *	* Fri Oct 25 23:11:56 JST 2013 Naoyuki Sawa
 *	- LedSeq_PlaySub()のバグを修正しました。
 *	* Fri Apr 04 23:43:32 JST 2014 Naoyuki Sawa
 *	- LedSeq_SetChAtt(),LedSeq_DirectNum()を追加しました。
 *	* Thu Aug 28 20:05:23 JST 2014 Naoyuki Sawa
 *	- LEDデバイス輝度セットアップ関数を廃止し、LedSeq_Exec()の引数としてバッファを指定するようにしました。
 *	  この変更により、バッファをスタック上に確保し易くなり、グローバル配列のメモリを節約し易くなりました。
 *	  (これまでは、セットアップ関数にバッファを伝えるために、バッファをグローバルに確保する必要が有った。)
 *	* Wed Nov 26 00:38:02 JST 2014 Naoyuki Sawa
 *	- LedConv_Typeの定義によって、LEDデータ形式を選択してビルドできるようにしました。
 */
#include "clip.h"

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

static ST_LedSeq stLedSeq;

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

static uint8_t LedSeq_PlaySub(ST_LedSeqCh* pLedSeqCh, uint8_t IntroLoop);

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

/* LEDシーケンサを初期化します。
 */
void LedSeq_Init() {
	uint16_t iLedDec;
	/* LEDシーケンサ全体をクリアします。 */
	memset(&stLedSeq, 0, sizeof stLedSeq);
	/* LEDデコーダプールを初期化します。 */
	for(iLedDec = 0; iLedDec < LedDec_Max; iLedDec++) {	/* 最後の要素をループに含めない。ループ条件は「<=」でなく「<」である。 */
		stLedSeq.LedDec[iLedDec].Next = iLedDec + 1;	/* 各要素を次要素へチェインする。 */
	}
	stLedSeq.LedDec[iLedDec/*=LedDec_Max*/].Next = LedDec_End;	/* 最後の要素を終端する。 */
}

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

/* LEDシーケンサを1フレーム分実行します。
 * [in]
 *	Buffer[LedDev_Max + 1]		輝度を格納するバッファ
 * [note]
 *	- アプリケーションは、当関数がバッファに格納した値を、LEDデバイスへ設定してください。
 *	- バッファ値は、1〜31の範囲です。
 *	  アプリケーションは、バッファ値として1〜31以外の値が存在しないと想定して構いません。
 */
void LedSeq_Exec(uint8_t Buffer[/*LedDev_Max + 1*/]) {
	uint8_t  iLedSeqCh, Value;
	uint16_t nFrameLen[2];
	uint16_t iLedDec, iLedDev;
	uint32_t nDataLen, uData;
	const uint8_t* pData;
	const uint32_t* pTable;
	ST_LedSeqCh* pLedSeqCh;
	ST_LedDec* pLedDec;
	/* 展開バッファをクリアします。 */
	memset(Buffer, 1, LedDev_Max + 1);	//0ではなく1でクリアする。0(透過)は格納しないので、結果として展開バッファの値は1〜31の範囲になる。アプリケーションは、0の値を考慮しなくて良い。
	/* 各LEDシーケンサチャネルについて… */
	for(iLedSeqCh = 0; iLedSeqCh <= LedSeqCh_Max; iLedSeqCh++) {
		/* LEDシーケンサチャネルを取得します。 */
		pLedSeqCh = &stLedSeq.LedSeqCh[iLedSeqCh];
		/* 停止中ならば、何もしません。 */
		if(!pLedSeqCh->pTable) { continue; }	//再生中or停止中を示すフィールドは、pDataではなくpTableであることに注意せよ
		/* LEDシーケンサチャネルから、LEDデータの先頭アドレスを取得します。 */
		pData = pLedSeqCh->pData;
		if(!pData) { DIE(); }	/* バグ */
		/* LEDデータから、イントロフレーム数(2byte)を取得します。 */
		nFrameLen[0]  = *pData++;
		nFrameLen[0] |= *pData++ << 8;
		/* LEDデータから、ループフレーム数(2byte)を取得します。 */
		nFrameLen[1]  = *pData++;
		nFrameLen[1] |= *pData++ << 8;
		/* LEDシーケンサチャネルの、フレーム番号が正常であることを確認します。 */
		if(pLedSeqCh->Frame >= nFrameLen[pLedSeqCh->IntroLoop]) { DIE(); }	/* バグ,又は,データ破損 */	//フレーム番号がフレーム数に到達したら、即ループ又は停止処理を行うので、フレーム番号＝フレーム数になることも無い。
		/* LEDシーケンサチャネルから、LEDデコーダチェインの先頭番号を取得します。 */
		iLedDec = pLedSeqCh->Root;	//いきなり終端(LedDec_End)である可能性も有ります。⇒LEDが1個も無いデータの場合、又は、LEDデコーダプールが空で再生開始しLEDデコーダチェインが一つも確保できなかった場合。
		/* 各LEDについて… */
		for(;;) {
			/* LEDデコーダチェインが終端ならば、抜けます。 */
			if(iLedDec == LedDec_End) { break; }	//LEDデータよりも先に、LEDデコーダチェインが終端になる可能性も有ります。⇒LEDデコーダプールが不足して、LEDデコーダチェインがLEDデータのLED数未満しか確保できなかった場合。
			/* LEDデコーダ番号が正常であることを確認します。 */
			if(iLedDec > LedDec_Max) { DIE(); }	/* バグ,又は,データ破損 */
			/* LEDデータから、LEDデバイス番号(2byte)を取得します。 */
			iLedDev  = *pData++;
			iLedDev |= *pData++ << 8;
			/* LEDデバイス番号が正常であることを確認します。 */
			if(iLedDev > LedDev_Max) { DIE(); }	/* バグ,又は,データ破損 */	//LEDデコーダチェインよりも先に、LEDデータが終端になることは有りません。
			/* LEDデコーダを取得します。 */
			pLedDec = &stLedSeq.LedDec[iLedDec];
#if   (LedConv_Type == 2) //「/clip/tool/dpavidot/Sample/2byteRLE版/LedConv2.lua」形式
			{
				/* LEDデータから、イントロ部分の圧縮データサイズ(3byte)を取得します。 */
				nDataLen  = *pData++;
				nDataLen |= *pData++ <<  8;
				nDataLen |= *pData++ << 16;
				/* イントロ部分の圧縮データを飛ばします。 */
				pData    += nDataLen;
				/* LEDデータから、ループ部分の圧縮データサイズ(3byte)を取得します。 */
				nDataLen  = *pData++;
				nDataLen |= *pData++ <<  8;
				nDataLen |= *pData++ << 16;
				/* ループ部分の圧縮データを飛ばします。 */
				pData    += nDataLen;
				/* 連長が1以上ならば… */
				if(pLedDec->RleLV >= (1<<5)) {
					/* 連長を1減らします。 */
					pLedDec->RleLV -= (1<<5);
				/* 連長が1未満ならば… */
				} else {
					/* 次の(連長|輝度)を取得します。 */
					pLedDec->RleLV = *pLedDec->pData++;
					if(pLedDec->RleLV >= (7<<5)) { pLedDec->RleLV += *pLedDec->pData++ << 5; }
				}
				/* 輝度を取得します。 */
				Value = pLedDec->RleLV & ((1<<5)-1);
			}
#elif (LedConv_Type == 3) //「/clip/tool/dpavidot/Sample/差分_連長_ﾊﾌﾏﾝ圧縮版/LedConv3.lua」形式
			{
				const uint8_t* src;
				ST_DeltaRunHuffmanDecoder stDeltaRunHuffmanDecoder;
				/* LEDデータから、イントロ部分の圧縮データサイズ/4(2byte)を取得します。 */
				nDataLen  = *pData++;
				nDataLen |= *pData++ << 8;
				/* イントロ部分の圧縮データ位置を覚えておきます。
				 * イントロ部分を再生中ならば、このままとなります。
				 * ループ部分を再生中ならば、ループ部分の圧縮データ位置で上書きします。 */
				src = pData;
				/* イントロ部分の圧縮データを飛ばします。 */
				pData    += nDataLen << 2;
				/* LEDデータから、ダミーのLED番号(2byte)を読み飛ばします。 */
				pData    += 2;
				/* LEDデータから、ループ部分の圧縮データサイズ/4(2byte)を取得します。 */
				nDataLen  = *pData++;
				nDataLen |= *pData++ << 8;
				/* ループ部分を再生中ならば、ループ部分の圧縮データ位置で上書きします。 */
				if(pLedSeqCh->IntroLoop) { src = pData; }
				/* ループ部分の圧縮データを飛ばします。 */
				pData    += nDataLen << 2;
				/* 輝度を取得します。 */
				stDeltaRunHuffmanDecoder.src	= src;	//ST_LedDecのサイズを節約するためsrcをST_LedDecに格納せず、当関数内で取得してST_DeltaRunHuffmanDecoderを組立てます。
				stDeltaRunHuffmanDecoder.srcPos	= pLedDec->srcPos;
				stDeltaRunHuffmanDecoder.run	= pLedDec->run;
				stDeltaRunHuffmanDecoder.delta	= pLedDec->delta << 3;
				stDeltaRunHuffmanDecoder.value	= pLedDec->value << 3;
				Value = DeltaRunHuffmanDecoder_Getc(&stDeltaRunHuffmanDecoder) >> 3;
				pLedDec->srcPos	= stDeltaRunHuffmanDecoder.srcPos;
				pLedDec->run	= stDeltaRunHuffmanDecoder.run;
				pLedDec->delta	= stDeltaRunHuffmanDecoder.delta >> 3;
				pLedDec->value	= stDeltaRunHuffmanDecoder.value >> 3;
			}
#else
#error LedConv_Typeの定義が不正です。
#endif
			/* 非透過ならば… */
			if(Value) {						//┐今後アセンブラ化する時は、
				//減衰前が既に消灯(1)ならば､減衰処理を行わない｡	//├比較一回とジャンプ二回の、
				if(Value != 1) {				//┘「cmp %rX,1」「jrlt L_STORE」「jreq L_NOSTORE」で処理できます。
					//減衰量が29以下ならば…
					if(pLedSeqCh->Att <= 29) {
						//減衰後の輝度を2(最小輝度)以上にする。
						Value = ((Value - pLedSeqCh->Att) >= 2) ? (Value - pLedSeqCh->Att) : 2;
					//減衰量が30以上ならば…
					} else {
						//減衰後の輝度を1(消灯)にする。
						Value = 1;
					}
				}
				//輝度を､展開ﾊﾞｯﾌｧへ格納する｡
				Buffer[iLedDev] = Value;
			}
			/* LEDデコーダから、LEDデコーダチェインの次番号を取得します。 */
			iLedDec = pLedDec->Next;
		}
		/* LEDシーケンサチャネルのフレーム番号を進め、現在の再生部分のフレーム数に到達したら… */
		if(++pLedSeqCh->Frame >= nFrameLen[pLedSeqCh->IntroLoop]) {
			/* ループ部分を開始,又は,再開します。ループ部分が無ければ… */
			if(!LedSeq_PlaySub(pLedSeqCh, 1)) {
				/* LEDデータテーブルから、次要素の値を取得します。 */
				pTable = pLedSeqCh->pTable;	//LedSeq_PlayTable()の処理で既に次要素へ進んでいる。
				uData  = *pTable;
				/* 終端でなければ… */
				if(uData) {
					/* 巻き戻しならば、LEDデータテーブルのアドレスを巻き戻します。 */
					if(uData <= LedTable_RewindMax) { pTable -= uData; }
					/* LEDデータテーブルを再生開始します。 */
					LedSeq_PlayTable(iLedSeqCh, pTable);	//pTableは次要素へ進んで格納される。
				/* 終端ならば… */
				} else {
					/* LEDシーケンサチャネルを停止します。 */
					LedSeq_Stop(iLedSeqCh);
				}
			}
		}
	}
	/* 各LEDデバイスについて… */
	for(iLedDev = 0; iLedDev <= LedDev_Max; iLedDev++) {
		/* 直接指定輝度を取得し、指定有りならば格納します。 */
		Value = stLedSeq.Direct[iLedDev];
		if(Value) { Buffer[iLedDev] = Value; }
	}
}

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

/* LEDデータテーブルを再生開始します。
 * [in]
 *	iLedSeqCh	LEDシーケンサチャネル番号(0〜LedSeqCh_Max)
 *	_pTable		LEDデータテーブルの先頭アドレス
 */
void LedSeq_PlayTable(uint8_t iLedSeqCh, const void* _pTable) {
	uint16_t iLedDec, iLedDev;
	uint32_t nDataLen;
	const uint8_t* pData;
	const uint32_t* pTable = _pTable;
	ST_LedSeqCh* pLedSeqCh;
	ST_LedDec* pLedDec;
	/* LEDシーケンサチャネル番号とLEDデータテーブルの先頭アドレスが正常であることを確認します。 */
	if((iLedSeqCh > LedSeqCh_Max) || (!pTable)) { DIE(); }
	/* LEDデータテーブルの先頭要素を取得し、次要素へ進めます。 */
	pData = (const uint8_t*)*pTable++;
	/* LEDデータテーブルの先頭要素は、有効なLEDデータでなければいけません。終端や巻き戻しであってはいけません。 */
	if((uint32_t)pData <= (uint32_t)LedTable_RewindMax) { DIE(); }	/* データ破損 */
	/* LEDシーケンサチャネルを確実に停止します。 */
	LedSeq_Stop(iLedSeqCh);
	/* LEDシーケンサチャネルを取得します。 */
	pLedSeqCh = &stLedSeq.LedSeqCh[iLedSeqCh];
	/* LEDデータテーブルのアドレスを格納します。 */
	pLedSeqCh->pTable = pTable;	//既に二要素目へ進んでいる。
	/* LEDデータの先頭アドレスを格納します。 */
	pLedSeqCh->pData = pData;
	/* イントロフレーム数(2byte)とループフレーム数(2byte)を飛ばします。 */
	pData += (2 + 2);
	/* LEDデコーダプールのチェイン先頭番号を取得します。 */
	iLedDec = stLedSeq.LedDec_Root;		//いきなり終端(LedDec_End)である可能性も有ります。⇒LEDデコーダプールが空である場合。
	/* ここに、最後のLEDデコーダのポインタを取得します。 */
	pLedDec = NULL;	//もし、LEDが1個も無いデータならば、NULLのままで以下のループを終える。そのようなデータは無駄だが、全LED透過データとして発生し得るので、許容せざるを得ない。
	/* 各LEDについて… */
	for(;;) {
		/* LEDデータから、LEDデバイス番号(2byte)を取得します。 */
		iLedDev  = *pData++;
		iLedDev |= *pData++ << 8;
		/* LEDデータが終端ならば、抜けます。 */
		if(iLedDev == 0xFFFF) { break; }
		/* LEDデバイス番号が正常であることを確認します。 */
		if(iLedDev > LedDev_Max) { DIE(); }	/* バグ,又は,データ破損 */
		/* LEDデコーダチェインが終端ならば、抜けます。 */
		if(iLedDec == LedDec_End) { TRACE("LedSeq_Play: LEDデコーダ不足\n"); break; }	//動作はするが、データの一部のLEDが再生されない。LedDec_Maxを増やすことを検討せよ。
		/* LEDデコーダ番号が正常であることを確認します。 */
		if(iLedDec > LedDec_Max) { DIE(); }	/* バグ,又は,データ破損 */
		/* LEDデコーダを取得します。 */
		pLedDec = &stLedSeq.LedDec[iLedDec];
#if   (LedConv_Type == 2) //「/clip/tool/dpavidot/Sample/2byteRLE版/LedConv2.lua」形式
		/* LEDデータから、イントロ部分の圧縮データサイズ(3byte)を取得します。 */
		nDataLen  = *pData++;
		nDataLen |= *pData++ <<  8;
		nDataLen |= *pData++ << 16;
		/* イントロ部分の圧縮データを飛ばします。 */
		pData    += nDataLen;
		/* LEDデータから、ループ部分の圧縮データサイズ(3byte)を取得します。 */
		nDataLen  = *pData++;
		nDataLen |= *pData++ <<  8;
		nDataLen |= *pData++ << 16;
		/* ループ部分の圧縮データを飛ばします。 */
		pData    += nDataLen;
#elif (LedConv_Type == 3) //「/clip/tool/dpavidot/Sample/差分_連長_ﾊﾌﾏﾝ圧縮版/LedConv3.lua」形式
		/* LEDデータから、イントロ部分の圧縮データサイズ/4(2byte)を取得します。 */
		nDataLen  = *pData++;
		nDataLen |= *pData++ << 8;
		/* イントロ部分の圧縮データを飛ばします。 */
		pData    += nDataLen << 2;
		/* LEDデータから、ダミーのLED番号(2byte)を読み飛ばします。 */
		pData    += 2;
		/* LEDデータから、ループ部分の圧縮データサイズ/4(2byte)を取得します。 */
		nDataLen  = *pData++;
		nDataLen |= *pData++ << 8;
		/* ループ部分の圧縮データを飛ばします。 */
		pData    += nDataLen << 2;
#else
#error LedConv_Typeの定義が不正です。
#endif
		/* LEDデコーダから、LEDデコーダチェインの次番号を取得します。 */
		iLedDec = pLedDec->Next;
	}
	/* LEDが1個も無いデータでなければ… */
	if(pLedDec) {
		/* LEDシーケンサチャネルが使用するLEDデコーダチェインを、LEDデコーダプールから確保します。 */
		pLedSeqCh->Root = stLedSeq.LedDec_Root;					//┓
				  stLedSeq.LedDec_Root = pLedDec->Next;			//┣LEDシーケンサチャネルが使用するLEDデコーダチェインを、LEDデコーダプールから抜き出す処理
							 pLedDec->Next = LedDec_End;	//┛
	/* LEDが1個も無いデータならば… */	//又は、LEDデコーダプールが空で再生開始しLEDデコーダチェインが一つも確保できなかった場合も、pLedDecがNULLのままこちらの処理となる。
	} else {
		/* LEDシーケンサチャネルの、LEDデコーダチェインの先頭番号を即終端します。 */
		pLedSeqCh->Root = LedDec_End;
	}
	/* イントロ部分を開始します。イントロ部分が無ければ… */
	if(!LedSeq_PlaySub(pLedSeqCh, 0)) {
		/* ループ部分を開始します。ループ部分も無ければ、エラー停止します。 */	//イントロ部分もループ部分も無いデータは許容しない。もしこれを許容すると、再生開始即終了のケースを考慮する必要が生じ、各処理が無駄に複雑になるからである。
		if(!LedSeq_PlaySub(pLedSeqCh, 1)) { DIE(); }	/* データ破損 */
	}
}

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

/* LEDデータを再生開始します。
 * [in]
 *	iLedSeqCh	LEDシーケンサチャネル番号(0〜LedSeqCh_Max)
 *	_pData		LEDデータの先頭アドレス
 * [note]
 *	- LEDデータ形式については、「clip/tool/dpavidot/Sample/2byteRLE版/LedConv2.bat」を参照してください。
 */
void LedSeq_Play(uint8_t iLedSeqCh, const void* pData) {
	static uint32_t Table[2];	//[0]を変更してLedSeq_PlayTable()を呼び出す。LedSeq_PlayTable()が処理を返したら[0]を破壊して良い。[1]はテーブルの終端要素として共有される。[1]を変更してはいけない。
	/* LEDシーケンサチャネル番号とLEDデータの先頭アドレスが正常であることを確認します。 */
	if((iLedSeqCh > LedSeqCh_Max) || (!pData)) { DIE(); }
	/* テーブルに、LEDデータの先頭アドレスを格納する。 */
	Table[0] = (uint32_t)pData;
	/* LEDデータ一個単独の、LEDデータテーブルの再生開始として処理する。 */
	LedSeq_PlayTable(iLedSeqCh, Table);
}

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

/* LEDシーケンサチャネルの減衰量を設定します。
 * [in]
 *	iLedSeqCh	LEDシーケンサチャネル番号(0〜LedSeqCh_Max)
 *	Att		減衰量(0〜255)
 * [note]
 *	- 実際には、(減衰量≧30)は全て最大減衰である。
 *	  LEDデータの最大輝度31から、減衰量30を引くと、輝度1(消灯)となるから。
 *	- LEDシーケンサチャネルの減衰量は、チャネルが再生・停止しても変化しない。
 *	  停止中のLEDシーケンサチャネルに対して減衰量を設定することも可能である。
 */
void LedSeq_SetChAtt(uint8_t iLedSeqCh, uint8_t Att) {
	ST_LedSeqCh* pLedSeqCh;
	/* LEDシーケンサチャネル番号が正常であることを確認します。 */
	if(iLedSeqCh > LedSeqCh_Max) { DIE(); }
	/* LEDシーケンサチャネルを取得します。 */
	pLedSeqCh = &stLedSeq.LedSeqCh[iLedSeqCh];
	/* LEDシーケンサチャネルに、減衰量を格納します。 */
	pLedSeqCh->Att = Att;
}

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

/* LEDシーケンサチャネルを停止します。
 * [in]
 *	iLedSeqCh	LEDシーケンサチャネル番号(0〜LedSeqCh_Max)
 */
void LedSeq_Stop(uint8_t iLedSeqCh) {
	uint16_t iLedDec;
	ST_LedSeqCh* pLedSeqCh;
	ST_LedDec* pLedDec;
	/* LEDシーケンサチャネル番号が正常であることを確認します。 */
	if(iLedSeqCh > LedSeqCh_Max) { DIE(); }
	/* LEDシーケンサチャネルを取得します。 */
	pLedSeqCh = &stLedSeq.LedSeqCh[iLedSeqCh];
	/* 停止中ならば、何もしません。 */
	if(!pLedSeqCh->pTable) { return; }	//再生中or停止中を示すフィールドは、pDataではなくpTableであることに注意せよ
	/* LEDシーケンサチャネルから、LEDデコーダチェインの先頭番号を取得します。 */
	iLedDec = pLedSeqCh->Root;	//いきなり終端(LedDec_End)である可能性も有ります。⇒LEDが1個も無いデータの場合、又は、LEDデコーダプールが空で再生開始しLEDデコーダチェインが一つも確保できなかった場合。
	/* ここに、最後のLEDデコーダのポインタを取得します。 */
	pLedDec = NULL;	//もし、LEDが1個も無いデータならば、NULLのままで以下のループを終える。そのようなデータは無駄だが、全LED透過データとして発生し得るので、許容せざるを得ない。
	/* 各LEDについて… */
	for(;;) {
		/* LEDデコーダチェインが終端ならば、抜けます。 */
		if(iLedDec == LedDec_End) { break; }	//LEDデータよりも先に、LEDデコーダチェインが終端になる可能性も有ります。⇒LEDデコーダプールが不足して、LEDデコーダチェインがLEDデータのLED数未満しか確保できなかった場合。
		/* LEDデコーダ番号が正常であることを確認します。 */
		if(iLedDec > LedDec_Max) { DIE(); }	/* バグ,又は,データ破損 */
		/* LEDデコーダを取得します。 */
		pLedDec = &stLedSeq.LedDec[iLedDec];
		/* LEDデコーダから、LEDデコーダチェインの次番号を取得します。 */
		iLedDec = pLedDec->Next;
	}
	/* LEDが1個も無いデータでなければ… */
	if(pLedDec) {
		/* LEDシーケンサチャネルが使用していたLEDデコーダチェイン全体を、LEDデコーダプールへ返却します。 */
		pLedDec->Next = stLedSeq.LedDec_Root;			//┓
				stLedSeq.LedDec_Root = pLedSeqCh->Root;	//┻LEDシーケンサチャネルが使用していたLEDデコーダチェイン全体を、LEDデコーダプールの先頭にチェインする処理
	}
	/* LEDシーケンサチャネルに、停止中をマークします。 */
	pLedSeqCh->pTable = NULL;	//再生中or停止中を示すフィールドは、pDataではなくpTableであることに注意せよ
}

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

/* LEDシーケンサチャネルを全て停止します。
 */
void LedSeq_StopAll() {
	uint8_t iLedSeqCh;
	/* 各LEDシーケンサチャネルについて… */
	for(iLedSeqCh = 0; iLedSeqCh <= LedSeqCh_Max; iLedSeqCh++) {
		/* LEDシーケンサチャネルを停止します。 */
		LedSeq_Stop(iLedSeqCh);
	}
}

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

/* LEDシーケンサチャネルの再生状態を取得します。
 * [in]
 *	iLedSeqCh	LEDシーケンサチャネル番号(0〜LedSeqCh_Max)
 * [out]
 *	戻り値		再生中ならば、1を返します。
 *			停止中ならば、0を返します。
 */
uint8_t LedSeq_Stat(uint8_t iLedSeqCh) {
	ST_LedSeqCh* pLedSeqCh;
	/* LEDシーケンサチャネル番号が正常であることを確認します。 */
	if(iLedSeqCh > LedSeqCh_Max) { DIE(); }
	/* LEDシーケンサチャネルを取得します。 */
	pLedSeqCh = &stLedSeq.LedSeqCh[iLedSeqCh];
	/* 再生中(1),又は,停止中(0)を返します。 */
	return pLedSeqCh->pTable != NULL;	//再生中or停止中を示すフィールドは、pDataではなくpTableであることに注意せよ
}

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

/* LEDデバイスに、直接指定輝度を設定します。
 * [in]
 *	iLedDev		LEDデバイス番号(0〜LedDev_Max)
 *	Value		直接指定輝度(0=指定無し,1=強制消灯,2〜31=強制点灯輝度)
 */
void LedSeq_Direct(uint16_t iLedDev, uint8_t Value) {
	/* LEDデバイス番号と直接指定輝度が正常であることを確認します。 */
	if((iLedDev > LedDev_Max) || (Value > ((1<<5)-1))) { DIE(); }
	/* 指定されたLEDデバイスに、直接指定輝度を格納します。 */
	stLedSeq.Direct[iLedDev] = Value;
}

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

/* 連続したLEDデバイスに、直接指定輝度を設定します。
 * [in]
 *	iLedDev		先頭のLEDデバイス番号(0〜LedDev_Max)
 *	nLedDev		連続したLEDデバイス数(0〜LedDev_Max+1)
 *	Value		直接指定輝度(0=指定無し,1=強制消灯,2〜31=強制点灯輝度)
 */
void LedSeq_DirectNum(uint16_t iLedDev, uint16_t nLedDev, uint8_t Value) {
	/* 先頭のLEDデバイス番号と連続したLEDデバイス数と直接指定輝度が正常であることを確認します。 */
	if(((iLedDev + nLedDev) > (LedDev_Max + 1)) || (Value > ((1<<5)-1))) { DIE(); }
	/* 連続したLEDデバイスに、直接指定輝度を格納します。 */
	memset(&stLedSeq.Direct[iLedDev], Value, nLedDev);
}

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

/* 全てのLEDデバイスに、直接指定輝度を設定します。
 * [in]
 *	Value		直接指定輝度(0=指定無し,1=強制消灯,2〜31=強制点灯輝度)
 */
void LedSeq_DirectAll(uint8_t Value) {
	LedSeq_DirectNum(0, LedDev_Max + 1, Value);
}

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

/* 再生開始とループの共通処理です。
 * [in]
 *	pLedSeqCh	LEDシーケンサチャネル
 *	IntroLoop	再生部分(0=イントロ部分,1=ループ部分)
 * [out]
 *	戻り値		再生開始したら、1を返します。
 *			指定された再生部分が無く、再生開始できなければ、0を返します。
 * [note]
 *	- 本関数は下記のフィールドを参照も変更もしません。
 *	    pLedSeqCh->pTable
 *	- 本関数は下記のフィールドを変更せず、既に設定されている値を参照します。
 *	    pLedSeqCh->pData      LEDデータの先頭アドレスが、既に設定されているものと想定します。
 *	    pLedSeqCh->Root       LEDデコーダチェインが、既に確保されているものと想定します。
 *	- 再生開始した場合、本関数は下記のフィールドを設定します。
 *	    pLedSeqCh->Frame      0を格納します。
 *	    pLedSeqCh->IntroLoop  指定された再生部分を格納します。
 */
static uint8_t LedSeq_PlaySub(ST_LedSeqCh* pLedSeqCh, uint8_t IntroLoop) {
	uint16_t nFrameLen[2];
	uint16_t iLedDec, iLedDev;
	uint32_t nDataLen;
	ST_LedDec* pLedDec;
	const uint8_t* pData;
	if(IntroLoop > 1) { DIE(); }	/* 呼び出し元のバグ */
	/* LEDシーケンサチャネルから、LEDデータの先頭アドレスを取得します。 */
	pData = pLedSeqCh->pData;
	if(!pData) { DIE(); }		/* 呼び出し元のバグ */
	/* LEDデータから、イントロフレーム数(2byte)を取得します。 */
	nFrameLen[0]  = *pData++;
	nFrameLen[0] |= *pData++ << 8;
	/* LEDデータから、ループフレーム数(2byte)を取得します。 */
	nFrameLen[1]  = *pData++;
	nFrameLen[1] |= *pData++ << 8;
	/* 指示された部分のフレーム数が0ならば、再生開始せずに0を返します。 */
	if(nFrameLen[IntroLoop] == 0) { return 0; }
	/* フレーム番号と再生部分を格納する。 */
	pLedSeqCh->Frame     = 0;	//Frame,IntroLoopは、停止時やフレーム数到達時にクリアされないので、前回の値が残っている。ここで明示的にクリアする必要がある。
	pLedSeqCh->IntroLoop = IntroLoop;
	/* LEDシーケンサチャネルから、LEDデコーダチェインの先頭番号を取得します。 */
	iLedDec = pLedSeqCh->Root;	//いきなり終端(LedDec_End)である可能性も有ります。⇒LEDが1個も無いデータの場合、又は、LEDデコーダプールが空で再生開始しLEDデコーダチェインが一つも確保できなかった場合。
	/* 各LEDについて… */
	for(;;) {
		/* LEDデコーダチェインが終端ならば、抜けます。 */
		if(iLedDec == LedDec_End) { break; }	//LEDデータよりも先に、LEDデコーダチェインが終端になる可能性も有ります。⇒LEDデコーダプールが不足して、LEDデコーダチェインがLEDデータのLED数未満しか確保できなかった場合。
		/* LEDデコーダ番号が正常であることを確認します。 */
		if(iLedDec > LedDec_Max) { DIE(); }	/* バグ,又は,データ破損 */
		/* LEDデータから、LEDデバイス番号(2byte)を取得します。 */
		iLedDev  = *pData++;
		iLedDev |= *pData++ << 8;
		/* LEDデバイス番号が正常であることを確認します。 */
		if(iLedDev > LedDev_Max) { DIE(); }	/* バグ,又は,データ破損 */	//LEDデコーダチェインよりも先に、LEDデータが終端になることは有りません。
		/* LEDデコーダを取得します。 */
		pLedDec = &stLedSeq.LedDec[iLedDec];
#if   (LedConv_Type == 2) //「/clip/tool/dpavidot/Sample/2byteRLE版/LedConv2.lua」形式
		{
			const uint8_t* src;
			/* LEDデータから、イントロ部分の圧縮データサイズ(3byte)を取得します。 */
			nDataLen  = *pData++;
			nDataLen |= *pData++ <<  8;
			nDataLen |= *pData++ << 16;
			/* イントロ部分の圧縮データ位置を覚えておきます。
			 * イントロ部分を再生中ならば、このままとなります。
			 * ループ部分を再生中ならば、ループ部分の圧縮データ位置で上書きします。 */
			src = pData;
			/* イントロ部分の圧縮データを飛ばします。 */
			pData    += nDataLen;
			/* LEDデータから、ループ部分の圧縮データサイズ(3byte)を取得します。 */
			nDataLen  = *pData++;
			nDataLen |= *pData++ <<  8;
			nDataLen |= *pData++ << 16;
			/* ループ部分を再生中ならば、ループ部分の圧縮データ位置で上書きします。 */
			if(pLedSeqCh->IntroLoop) { src = pData; }
			/* ループ部分の圧縮データを飛ばします。 */
			pData    += nDataLen;
			/* LEDデコーダを初期化します。 */
			pLedDec->pData = src;
			pLedDec->RleLV = 0;	//これを忘れると、前回の(連長|輝度)の残りを使ってしまうバグになる。注意せよ。
		}
#elif (LedConv_Type == 3) //「/clip/tool/dpavidot/Sample/差分_連長_ﾊﾌﾏﾝ圧縮版/LedConv3.lua」形式
		{
			const uint8_t* src;
			ST_DeltaRunHuffmanDecoder stDeltaRunHuffmanDecoder;
			/* LEDデータから、イントロ部分の圧縮データサイズ/4(2byte)を取得します。 */
			nDataLen  = *pData++;
			nDataLen |= *pData++ << 8;
			/* イントロ部分の圧縮データ位置を覚えておきます。
			 * イントロ部分を再生中ならば、このままとなります。
			 * ループ部分を再生中ならば、ループ部分の圧縮データ位置で上書きします。 */
			src = pData;
			/* イントロ部分の圧縮データを飛ばします。 */
			pData    += nDataLen << 2;
			/* LEDデータから、ダミーのLED番号(2byte)を読み飛ばします。 */
			pData    += 2;
			/* LEDデータから、ループ部分の圧縮データサイズ/4(2byte)を取得します。 */
			nDataLen  = *pData++;
			nDataLen |= *pData++ << 8;
			/* ループ部分を再生中ならば、ループ部分の圧縮データ位置で上書きします。 */
			if(pLedSeqCh->IntroLoop) { src = pData; }
			/* ループ部分の圧縮データを飛ばします。 */
			pData    += nDataLen << 2;
			/* LEDデコーダを初期化します。 */
			DeltaRunHuffmanDecoder_Init(&stDeltaRunHuffmanDecoder, src);
			pLedDec->srcPos	= stDeltaRunHuffmanDecoder.srcPos;
			pLedDec->run	= stDeltaRunHuffmanDecoder.run;
			pLedDec->delta	= stDeltaRunHuffmanDecoder.delta >> 3;
			pLedDec->value	= stDeltaRunHuffmanDecoder.value >> 3;
		}
#else
#error LedConv_Typeの定義が不正です。
#endif
		/* LEDデコーダから、LEDデコーダチェインの次番号を取得します。 */
		iLedDec = pLedDec->Next;
	}
	/* 再生開始したら1を返します。 */
	return 1;
}

