/*
 *	clipsndm.h
 *
 *	Sound Manager
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2013 Naoyuki Sawa
 *
 *	* Sat Jul 27 21:43:04 JST 2013 Naoyuki Sawa
 *	- 1st リリース。
 *	* Thu Aug 01 00:52:48 JST 2013 Naoyuki Sawa
 *	- 関数の説明を追記しました。
 *	* Thu Sep 12 23:08:12 JST 2013 Naoyuki Sawa
 *	- SndMgr_AttCh()が、チャネルが再生中の場合のみ処理を行うように、修正しました。
 *	- 修正前のコードは、チャネルが停止中であっても、処理を行ってしまっていました。
 *	  発声していないチャネルに対してfnAtt()を実行するという、無駄がありました。(実害は無いが)
 *	* Fri Sep 13 01:48:44 JST 2013 Naoyuki Sawa
 *	- SndMgr_AttMaster()を追加しました。
 */
#ifndef __CLIP_SNDM_H__
#define __CLIP_SNDM_H__

/****************************************************************************
 *	定数、構造体
 ****************************************************************************/

/* 初期化情報 */
typedef struct _ST_SndMgrInfo {
	void (*fnPlay)(int iCh, int iData, int iLoop);	/* + 0,4 */
	void (*fnStop)(int iCh);			/* + 4,4 */
	int  (*fnStat)(int iCh);			/* + 8,4 */
	void (*fnAtt )(int iCh, int attNow);		/* +12,4 */
} ST_SndMgrInfo;					/* =16 */

/* チャネル構造体 */
typedef struct _ST_SndMgrCh {
	         short iLoop;		/* + 0,2 再生開始時のループ指定を記憶 ((=0)停止中,(>0)有限ループ再生中,(<0)無限ループ再生中) */
	unsigned short iData;		/* + 2,2 再生中のデータ番号 ((0〜USHRT_MAX)全て有効なデータ番号とする。特別扱いする値は無い) */
	unsigned short uKeep;		/* + 4,2 再生維持重要度 (USHRT_MAX⇒0) */
	unsigned short attPeriod;	/* + 6,2 変化に要する時間 (0=変化無し) */
	unsigned short attTime;		/* + 8,2 変化の経過時間 (0⇒attPeriod) */
	unsigned char  attOld;		/* +10,1 変化前の減衰量 (0〜64) */
	unsigned char  attNew;		/* +11,1 変化後の減衰量 (0〜64) */
} ST_SndMgrCh;				/* =12 */

/* Sound Manager構造体 */
typedef struct _ST_SndMgr {
	const ST_SndMgrInfo* pInfo;		/* +0,4 初期化情報へのポインタ */
	unsigned char MinCh;			/* +4,1 下限チャネル番号 */
	unsigned char MaxCh;			/* +5,1 上限チャネル番号 (この番号も含みます) */
	unsigned char attMaster;		/* +6,1 全体の減衰量 (0〜64) */
	/* unsigned char Padding; */		/* +7,1 */
	ST_SndMgrCh Ch[0/*MaxCh-MinCh+1*/];	/* +8,12*(MaxCh-MinCh+1) */
} ST_SndMgr;					/* =8+12*(MaxCh-MinCh+1) */

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

/* Sound Managerを作成します。
 * [in]
 *	pInfo		初期化情報へのポインタ
 *	MinCh		下限チャネル番号
 *	MaxCh		上限チャネル番号 (この番号も含みます)
 * [out]
 *	戻り値		Sound Manager
 * [note]
 *	- pInfoが指す内容をSound Manager構造体の中にコピーせず、ポインタのままで保持します。
 *	  アプリケーションは、SndMgr_Init()が処理を返した後も、pInfoを開放してはいけません。
 *	- こういう設計にした理由は、同じデバイス(sound or adpcm)の、異なるチャネル範囲を指す、
 *	  複数のSound Managerから、1個の初期化情報を共有し、メモリ節約できると考えたためです。
 */
ST_SndMgr* SndMgr_Init(const ST_SndMgrInfo* pInfo, int MinCh, int MaxCh);


/* Sound Managerを開放します。
 * [in]
 *	pSndMgr		Sound Manager
 *			本関数のみ、pSndMgr引数にNULLを指定可能です。
 *			「SndMgr_Free(NULL)」の呼び出しは安全であり、何も行いません。
 * [note]
 *	- このSound Managerが管理するチャネル範囲を全て再生停止した後、メモリを開放します。
 *	- アプリケーション終了直前ならば、明示的にSound Managerを開放する必要はありません。
 *	  Sound Managerはメモリリソースのみ使用し、割り込みフック等は行っていないからです。
 */
void SndMgr_Free(ST_SndMgr* pSndMgr);

/* Sound Managerの1フレーム処理を行います。
 * [in]
 *	pSndMgr		Sound Manager
 * [note]
 *	- 本関数は、具体的には以下の処理を行います:
 *	・再生完了したチャネルを、再生停止中に戻す
 *	・再生継続時間の監視 (再生維持重要度の減少)
 *	・フェードインフェードアウト (減衰量の変化)
 */
void SndMgr_Exec(ST_SndMgr* pSndMgr);

/* チャネル番号を指定して、再生開始します。
 * [in]
 *	pSndMgr		Sound Manager
 *	iCh		チャネル番号 (MinCh≦iCh≦MaxCh)
 *	iData		データ番号 (pInfo->fnPlayによって解釈する、アプリケーション定義の値)
 *	iLoop		(1〜SHRT_MAX)=有限ループ、(-1)=無限ループ。左記以外の値は動作未定義
 * [note]
 *	- 本関数は、具体的には以下の処理を行います:
 *	・指定されたチャネルの現在の状態に関係なく、新しい再生を開始します。
 *	・指定されたチャネルの減衰量を、即、0(=音量最大)に設定します。
 *	・他のチャネルに影響を与えません。同じデータ番号を再生中のチャネルが他に有っても、関係ありません。
 */
void SndMgr_PlayCh(ST_SndMgr* pSndMgr, int iCh, int iData, int iLoop);

/* データ番号を指定して、再生開始します。
 * [in]
 *	pSndMgr		Sound Manager
 *	iData		データ番号 (pInfo->fnPlayによって解釈する、アプリケーション定義の値)
 *	iLoop		(1〜SHRT_MAX)=有限ループ、(-1)=無限ループ。左記以外の値は動作未定義
 * [note]
 *	- 本関数は、具体的には以下の処理を行います:
 *	・指定されたデータ番号を再生中のチャネル全てを、即停止します。
 *	・それによって停止したチャネル，又は，既に停止中のチャネルが有れば、そのチャネルで再生開始します。
 *	・停止チャネルが無い場合、再生維持重要度が最小のチャネルを差し替えて、再生開始します。
 *	  ただし、既存の無限ループを、新しい有限ループに差し替えることはしません。(実装を参照してください)
 *	・再生開始した場合、再生開始したチャネルの減衰量を、即、0(=音量最大)に設定します。
 */
void SndMgr_PlayData(ST_SndMgr* pSndMgr, int iData, int iLoop);

/* チャネル番号を指定して、再生停止します。
 * [in]
 *	pSndMgr		Sound Manager
 *	iCh		チャネル番号 (MinCh≦iCh≦MaxCh)
 * [note]
 *	- 指定されたチャネルの現在の状態に関係なく、確実に停止します。
 */
void SndMgr_StopCh(ST_SndMgr* pSndMgr, int iCh);

/* データ番号を指定して、再生停止します。
 * [in]
 *	pSndMgr		Sound Manager
 *	iData		データ番号 (pInfo->fnPlayによって解釈する、アプリケーション定義の値)
 * [note]
 *	- 指定されたデータ番号を再生中のチャネル全てを、即停止します。
 */
void SndMgr_StopData(ST_SndMgr* pSndMgr, int iData);

/* 全チャネルを、再生停止します。
 * [in]
 *	pSndMgr		Sound Manager
 */
void SndMgr_StopAll(ST_SndMgr* pSndMgr);

/* チャネル番号を指定して、減衰量を設定します。
 * [in]
 *	pSndMgr		Sound Manager
 *	iCh		チャネル番号 (MinCh≦iCh≦MaxCh)
 *	attNew		最終減衰量
 *	attPeriod	変化時間[フレーム] (0=即完了)
 * [note]
 *	- 前回の変化が未完了ならば、即、前回の最終減衰量を設定した後、今回の変化を開始します。
 *	  要するに、前回の変化が完了か未完了かにかかわらず、今回の変化の傾きは同じになります。
 */
void SndMgr_AttCh(ST_SndMgr* pSndMgr, int iCh, int attNew, int attPeriod);

/* データ番号を指定して、減衰量を設定します。
 * [in]
 *	pSndMgr		Sound Manager
 *	iData		データ番号 (pInfo->fnPlayによって解釈する、アプリケーション定義の値)
 *	attNew		最終減衰量
 *	attPeriod	変化時間[フレーム] (0=即完了)
 * [note]
 *	- 指定されたデータ番号を再生中のチャネル全てに対し、減衰量を設定します。
 *	- 変化の傾きの特性については、SndMgr_AttCh()の[note]を参照してください。
 */
void SndMgr_AttData(ST_SndMgr* pSndMgr, int iData, int attNew, int attPeriod);

/* 全チャネルの、減衰量を設定します。
 * [in]
 *	pSndMgr		Sound Manager
 *	attNew		最終減衰量
 *	attPeriod	変化時間[フレーム] (0=即完了)
 * [note]
 *	- 変化の傾きの特性については、SndMgr_AttCh()の[note]を参照してください。
 */
void SndMgr_AttAll(ST_SndMgr* pSndMgr, int attNew, int attPeriod);

/* 全体の減衰量を設定します。
 * [in]
 *	pSndMgr		Sound Manager
 *	attMaster	全体の減衰量
 * [note]
 *	- 当関数で設定する「全体の減衰量」と、SndMgr_AttCh()等で設定する「チャネル単位の減衰量」の合計が、各チャネルの実効減衰量になります。
 *	- 当関数で設定する「全体の減衰量」の目的は、Sound Manager単位の"トータルボリューム調整"を意図したものです。
 *	  演出用の機能ではないので、「全体の減衰量」を連続的に変化する機能はありません。
 */
void SndMgr_AttMaster(ST_SndMgr* pSndMgr, int attMaster);

/****************************************************************************
 *	使用例
 ****************************************************************************/

//	//データ番号を解釈するためのスタブ関数
//	static void sound_play_stub(int iCh, int iData, int iLoop) {
//		if((unsigned)iData >= SOUND_COUNT) { DIE(); }
//		sound_play(iCh, sound_table[iData], iLoop);
//	}
//	//テスト初期化関数
//	void test_init() {
//		static const ST_SndMgrInfo stSndMgrInfo = { sound_play_stub, sound_stop, sound_stat, sound_att };
//		pSndMgrBgm = SndMgr_Init(&stSndMgrInfo, 0, 0); //BGM Sound Manager
//		pSndMgrSe  = SndMgr_Init(&stSndMgrInfo, 1, 3); //SE  Sound Manager
//	}
//	//テストメイン関数
//	void test_main() {
//		test_init();
//		SndMgr_AttMaster(pSndMgrBgm, 6); //BGM Sound Manager全体を約50%減衰
//		SndMgr_AttMaster(pSndMgrSe , 3); //SE  Sound Manager全体を約30%減衰
//		for(;;) {
//			schedule();
//			SndMgr_Exec(pSndMgrBgm); //BGM Sound Manager
//			SndMgr_Exec(pSndMgrSe ); //SE  Sound Manager
//			if(joy & PAD_A) {
//				if(joy & TRG_LF) { SndMgr_PlayCh(pSndMgrBgm, 0, SND_BGM1, -1); } //BGM1をループ再生
//				if(joy & TRG_RI) { SndMgr_PlayCh(pSndMgrBgm, 0, SND_BGM2, -1); } //BGM2をループ再生
//				if(joy & TRG_UP) { SndMgr_PlayCh(pSndMgrBgm, 0, SND_JNGL,  1); } //ジングルを一回再生
//				if(joy & TRG_DN) { SndMgr_StopCh(pSndMgrBgm, 0); } //再生停止
//				if(joy & TRG_START) { SndMgr_AttCh(pSndMgrBgm, 0, 64, SEC(1)); } //一秒間かけて消音
//			}
//			if(joy & PAD_B) {
//				if(joy & TRG_LF) { SndMgr_PlayData(pSndMgrSe, SND_SE1, 1); } //SE1を一回再生。開きチャネルが無い場合は一番古い音を差し替え
//				if(joy & TRG_RI) { SndMgr_PlayData(pSndMgrSe, SND_SE2, 1); } //SE2を一回再生。開きチャネルが無い場合は一番古い音を差し替え
//				if(joy & TRG_UP) { SndMgr_PlayData(pSndMgrSe, SND_SE3, 1); } //SE3を一回再生。開きチャネルが無い場合は一番古い音を差し替え
//				if(joy & TRG_DN) { SndMgr_PlayData(pSndMgrSe, SND_SE4, 1); } //SE4を一回再生。開きチャネルが無い場合は一番古い音を差し替え
//				if(joy & TRG_START) { SndMgr_AttData(pSndMgrBgm, SND_SE1, 64, SEC(1)); } //SE1を再生中のチャネルが有れば、一秒間かけて消音
//			}
//		}
//	}

#endif/*__CLIP_SNDM_H__*/
