/*
 *	clipxmp1.h
 *
 *	YAMAHA YMZ871 XMP-1 (eXtensible Music Player - 1) 簡易ドライバ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2017 Naoyuki Sawa
 *
 *	* Thu Oct 13 21:52:21 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- 既存の、TAPシーケンサ,TAPミキサー,タイマ管理をモジュール利用して、YMZ871を制御する簡易ドライバです。
 *	  YMZ871の特性については、以前に、cliptaps.cの中にもコメントを書いているので、そちらも参照して下さい。
 *	* Thu Feb 09 23:42:37 JST 2017 Naoyuki Sawa
 *	- cliptaps.cモジュールにおいてST_TapSeqInfo構造体を変更した事に伴い、Xmp1_Init()の処理も変更しました。
 *	* Sun Apr 02 22:50:54 JST 2017 Naoyuki Sawa
 *	- cliptaps.cに再生準備通知を追加した事に伴い、Xmp1_Init()内のstTapSeqInfoの定義を変更しました。
 *	  当モジュールでは再生準備通知を利用しません。
 *	  コンパイルエラーを防ぐためだけに変更ました。
 */
#ifndef __CLIP_XMP1_H__
#define __CLIP_XMP1_H__
#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus
/*****************************************************************************
 *	定数
 *****************************************************************************/
//シーケンサのコンフィグレーション
#define Xmp1_TapDevCh_Max		((32/2)-1)				//デバイスチャネル番号の最大値		変更不可	XMP-1の全32トラックを、2トラックずつのペアにして、モノラルフレーズ/ステレオフレーズ両対応のデバイスチャネルとして使用する事にしました。		モノラルフレーズを再生する場合も2トラックを占有する事になり、効率は良くない。実装を簡単にするためにこのようにしたが、今後、変更を検討すべきである。
#define Xmp1_TapLogCh_Max		Xmp1_TapDevCh_Max			//論理チャネル番号の最大値		変更不可	論理チャネル数とデバイスチャネルを、1:1で割り当てる事にしました。つまり、TAPの機能は使用しない事にしました。
#define Xmp1_TapSeqCh_Max		Xmp1_TapLogCh_Max/*調整可*/		//シーケンサチャネル番号の最大値	調整可		シーケンサチャネルと論理チャネルを、1:1で制御する事が可能な数の、シーケンサチャネルを確保する事にしました。必ずしもそのように使用する必要は無く、一つのシーケンサチャネルで複数の論理チャネルを制御しても構いません。アプリケーション定義の(ST_Xmp1Info.getMxrChMask)が返す値によって変更可能です。
#define Xmp1_TapSeq_iCont		((1000+(30-1))/30)/*調整可*/		//ループフレーズ引き継ぎ時間[ms]	調整可
#define Xmp1_TapSeq_nEvt		31/*調整可*/				//イベント記録数			調整可
#define Xmp1_TapSeq_iEvtMsk		(~(1<<TapEvt_Ctrl))/*調整可*/		//イベントマスク			調整可
/*---------------------------------------------------------------------------*/
//ミキサのコンフィグレーション
#define Xmp1_TapMxrCh_Max		Xmp1_TapLogCh_Max/*調整可*/		//ミキサーチャネル番号の最大値		調整可		ミキサーチャネルと論理チャネルを、1:1で制御する事が可能な数の、ミキサーチャネルを確保する事にしました。必ずしもそのように使用する必要は無く、一つのミキサーチャネルで複数の論理チャネルを制御しても構いません。
/*---------------------------------------------------------------------------*/
#define Xmp1_Exec_Xms_uItv		8/*調整可*/				//1msタイマ関数のインターバル		調整可		シーケンサの処理は、タイマ割り込み処理として正確な周期で実行したいので、1msタイマ関数として登録する。インターバルは大きい方が負荷が小さい。音の切り替え時等で違和感が出ない限りで、なるべく大きな値に調整せよ。
#define Xmp1_Exec_X00ms_uItv		1/*調整可*/				//100msタイマ関数のインターバル		調整可		ミキサーの処理は粗くても目立たないので、タイマ割り込み処理ではなくTimMgr_Exec()から呼び出されるように、100msタイマ関数として登録する。もし実際に聴いてみて粗が目立つ場合は、10msタイマ関数に変更しても構わない。
#define Xmp1_VolumeSw_Stable_ms		10/*調整可*/				//ボリュームスイッチの安定時間[ms]	調整可
#define Xmp1_BusyToReady_ms		10/*調整可*/				//ビジー期間の時間[ms]			調整可		10[ms]とした理由について、下記のコメントを参照せよ。
//■参照資料「YMZ871 ｱﾌﾟﾘｹｰｼｮﾝﾏﾆｭｱﾙ CATALOG No. LSI-5MZ871A40 2013.9」(5MZ871A40.pdf)の、「7.1.2. フレーズ再生、シーケンサー」(p.232)より引用
//│7.1.2. フレーズ再生、シーケンサー
//│8. すでにフレーズ再生中のトラックにおいて後着優先再生を実行し、さらに続けて先着・後着優先再生を実行する場合には、後着優先再生と続く先着・後着優先再生の間に96サンプル時間の間隔をあけてください。
//96サンプルとは「(1/44100[Hz])*96[サンプル]=2.18[ms]」であり、仕様上はビジー期間の時間は3[ms]であるはずなのだが、実際に試すと3[ms]では不足なようで音が消えてしまう事が有った。
//5[ms]でもまだ不足であり、10[ms]待つとようやく音が消える事が無くなった。仕様上の値と差が大きい点が謎だが、仕方が無いので10[ms]待つ事にした。
/*---------------------------------------------------------------------------*/
//ST_Xmp1Trk.Flag
#define Xmp1Trk_Flag_Busy		(1<<0)					//このトラックがビジーである事を示す。ビジーとは、再生開始後96サンプル(約2.2ms)以内の期間を意味する。「YMZ871 ｱﾌﾟﾘｹｰｼｮﾝﾏﾆｭｱﾙ CATALOG No. LSI-5MZ871A40 2013.9」(5MZ871A40.pdf)の、「7.1.2. フレーズ再生、シーケンサー」(p.231)を参照せよ。
#define Xmp1Trk_Flag_Pending		(1<<1)					//ビジー待ちフレーズが有る事を示す。ビジー待ちフレーズとは、このトラックがビジーである間に、再生要求が行われたフレーズを意味する。このトラックがビジーである間に複数回の再生要求が行われた場合は、最後に再生要求が行われたフレーズを示す。
#define Xmp1Trk_Flag_Stereo		(1<<2)					//再生中のフレーズ,又は,ビジー待ちフレーズがステレオフレーズである事を示す。このフラグがセットされていなければモノラルフレーズである。
/*****************************************************************************
 *	構造体
 *****************************************************************************/
//初期化情報
typedef struct _ST_Xmp1Info {
	uint8_t			(*readXmp1Reg)(uint8_t rb, uint8_t index);			//XMP-1のレジスタを読み出す。				rb=0〜6(レジスタバンク),index=0x00〜0xFF(レジスタ番地)				※未使用:2016/10/13現在の実装では使用していません。
	void			(*writeXmp1Reg)(uint8_t rb, uint8_t index, uint8_t data);	//XMP-1のレジスタに書き込む。				rb=0〜6(レジスタバンク),index=0x00〜0xFF(レジスタ番地),data=0x00〜0xFF(データ)
	uint8_t			(*getMsecTime)();						//現在時刻[ms]を返す。					戻り値は0x00〜0xFFでオーバーラップする値として下さい。				LSI(XMP-1)のビジー期間を判定する処理にも使用している。従って、精度が重要である。マイコン内蔵のハードウェアタイマのレジスタ値等を、直接読み出して返すのが望ましい。割り込み駆動のソフトウェアカウンタでは、割り込み禁止による誤差が生じるため望ましくない。
	uint8_t			(*getVolumeSw)();						//ボリュームスイッチのポート値を返す。			戻り値は0x00〜0xFF(小〜大)として下さい。					実際のボリュームスイッチのハードウェアの解像度が0x00〜0xFFでない場合は、0x00〜0xFFに変換して返してください。
	int			(*getMxrChMask)(int iLogCh);					//論理チャネルに適用するミキサーチャネルマスクを返す。	戻り値は(1<<0)〜(1<<Xmp1_TapMxrCh_Max)の0個以上の組み合わせとして下さい。	例えば、ミキサーチャネルと論理チャネルを1:1で制御する単純な構成ならば、「return 1<<iLogCh;」でも構いません。
	uint16_t		_RegKey_PhrDef;							//RegKey_PhrDef						┐
	uint16_t		_RegKey_Next;							//RegKey_Next						┴「/clip/test.win/snd/MkPhrNo.bat」等で生成する情報です。
	uint16_t		_RegKey_No;							//RegKey_No						┐
	uint16_t		_RegKey_Channel;						//RegKey_Channel					├「/clip/tool/XmpPmiCnv.bat -x」で生成する情報です。
	uint16_t		_RegKey_Time;							//RegKey_Time						┘
	//↑↑ここまでのフィールドはアプリケーションのビルド時に静的に定義可能です。↑↑
	const uint8_t*		TBL_RegTbl;							//レジストリテーブルへのポインタ			Win32プロジェクトで、レジストリテーブルをリソースから取得する場合は、このフィールドだけは動的に設定する必要が有り、ST_Xmp1Infoをconstには出来ません。P/ECEやHewプロジェクトで、レジストリテーブルをバイナリリンクする場合は、このフィールドも静的に定義可能であり、ST_Xmp1Info全体をconstとしてROMに配置する事が出来ます。
} ST_Xmp1Info;
/*---------------------------------------------------------------------------*/
//トラック構造体
typedef struct _ST_Xmp1Trk {
	uint8_t			Flag;						//フラグ				(Xmp1Trk_Flag_*)の0個以上の組み合わせ。
	uint8_t			BusyTime;					//ビジー判定基準時刻[ms]		((Flag&Xmp1Trk_Flag_Busy)<>0)ならば、ビジー判定基準時刻[ms]を示す。((Flag&Xmp1Trk_Flag_Busy)==0)ならば、このフィールドは意味を持たない。
	int16_t			PN;						//再生中フレーズ番号			0〜8191=再生中フレーズ番号,-1=無し	((Flag&Xmp1Trk_Flag_Pending)<>0)ならば、ビジー待ちフレーズのフレーズ番号を示す。
	uint8_t			P0;						//左右パン				左スピーカーの音量:0x00〜0x7F=大〜小,0x80=消音,0x81=大(ステレオ左),0x82=消音(ステレオ右),0x83〜0xFF=消音／右スピーカーの音量:0x00=消音,0x01〜0x7F=小〜大,0x81=消音(ステレオ左),0x82=大(ステレオ右),0x83〜0xFF=消音
	uint8_t			V1;						//1次ボリューム				0x00=消音,0x01〜0x80=小〜大,0x81〜0xFF=設定禁止(実際には0x00と同じ)
	uint8_t			V2;						//2次ボリューム				0x00=消音,0x01〜0x80=小〜大,0x81〜0xFF=設定禁止(実際には0x00と同じ)
	uint8_t			LOOP;						//ループ回数				0x00〜0xFE=1〜255回,0xFF=無限
} ST_Xmp1Trk;
/*---------------------------------------------------------------------------*/
//管理構造体
typedef struct _ST_Xmp1 {
	const ST_Xmp1Info*	pInfo;						//初期化情報へのポインタ
	uint8_t			Vol;						//ボリュームスイッチの安定値
	uint8_t			NewVol;						//ボリュームスイッチのポート値
	uint8_t			NewVolTime;					//ボリュームスイッチの変化時刻[ms]
	ST_Xmp1Trk		TBL_Xmp1Trk[(Xmp1_TapDevCh_Max+1)];		//トラック配列				デバイスチャネルとトラックが、1:1に対応します。(変更不可)
	//qシーケンサ
	ST_TapSeq		_stTapSeq;					//シーケンサ
	ST_TapDevCh		_TBL_DevCh[(Xmp1_TapDevCh_Max+1)];		//デバイスチャネル配列			デバイスチャネルとトラックが、1:1に対応します。(変更不可)
	ST_TapLogCh		_TBL_LogCh[(Xmp1_TapLogCh_Max+1)];		//論理チャネル配列			2016/10/13現在の実装では、論理チャネルとデバイスチャネルが、1:1に対応しています。(変更可能)
	ST_TapSeqCh		_TBL_SeqCh[(Xmp1_TapSeqCh_Max+1)];		//シーケンサチャネル配列
	ST_TapEvtBuf		_EvtBuf;					//イベントバッファ
	ST_TapEvt		_TBL_Buf[(Xmp1_TapSeq_nEvt+1)];			//イベント配列
	//ミキサー
	ST_TapMxr		_stTapMxr;					//ミキサー
	ST_TapMxrCh		_TBL_MxrCh[(Xmp1_TapMxrCh_Max+1)];		//ミキサーチャネル配列
	//関数ノード
	ST_TimMgrFunc		stTimMgrFunc_TapSeq;				//シーケンサのタイマ関数登録用
	ST_TimMgrFunc		stTimMgrFunc_TapMxr;				//ミキサーのタイマ関数登録用
} ST_Xmp1;
/*****************************************************************************
 *	グローバル関数
 *****************************************************************************/
void Xmp1_Init(ST_Xmp1* pXmp1, const ST_Xmp1Info* pInfo, ST_TimMgr* pTimMgr);
ST_TapSeq* Xmp1_GetTapSeq(ST_Xmp1* pXmp1);
ST_TapMxr* Xmp1_GetTapMxr(ST_Xmp1* pXmp1);
#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus
#endif//__CLIP_XMP1_H__
