/*
 *		ADXT Sample No.5(Streaming by ADX file system)
 *
 *		音声を再生しながら、別のファイルを少しずつ読み出します。
 *		テキストファイルを読み込み、1行づつ表示します。
 *
 */

/***************************************************************************
 *      インクルードファイル
 *      Include file
 ***************************************************************************/
#include	"shinobi.h"
#include	"sg_sd.h"
#include	"sj.h"
#include	"cri_adxf.h"
#include	"cri_adxt.h"

/***************************************************************************
 *      定数マクロ
 *      MACRO CONSTANT
 ***************************************************************************/

/*	最大チャンネル数				*/
/*	The number of maximum channels	*/
#define	MAX_CH			(2)

/*	同時再生最大CDストリーム数									*/
/*	The number of maximum CD streams playing at the same time	*/
#define	MAX_CDSTM		(1)

/*	最高サンプリング周波数		*/
/*	Maximum sampling frequency	*/
#define	MAX_SFREQ		(44100)

/*	ファイル再生用ワーク領域の大きさ	*/
/*	Work area size of CD stream play	*/
#define	WKSIZE		(ADXT_CALC_WORK(MAX_CH, 1, MAX_CDSTM, MAX_SFREQ))

/***************************************************************************
 *      変数定義
 *		Variable Declaration
 ***************************************************************************/

/*	ファイル再生用 ADXT ワーク領域	*/
/*	Work area						*/
char work[WKSIZE];

/***************************************************************************
 *      関数定義
 *      Function Declaration
 ***************************************************************************/

/***
 *		サウンド環境の初期化
 *		Initialize sound section
 ***/

/*	サウンドドライバの転送・初期化					*/
/*	Initialize sound library and load sound driver	*/
void	sound_init(void *ptr)
{
	SDMEMBLK	memblk = NULL;

	/*	Initialize Sound library	 */
	sdLibInit(NULL, 0, 0);		/*	This api's argument is dummy now.  */

	/* 	Download and Initialize Sound driver.	 */
	/*	Create Memory block handle  */
	sdMemBlkCreate(&memblk);
	/*  Setting Memoby block handle  */
	sdMemBlkSetPrm(memblk, ptr, 0, SDD_MEMBLK_SYNC_FUNC, NULL);
	/*	Initialize Sound driver  */
	sdDrvInit(memblk);
	sdMemBlkDestroy(memblk);
}

/***
 *			ユーティリティ関数
 *			Utility function
 ***/

/*	データの読み込み	*/
/*	Load data to memory	*/
void *load_file(char *fname, long *len)
{
	GDFS gdfs;
	long fsize, nsct;
	void *dat=NULL;

	if ( (gdfs=gdFsOpen(fname, NULL)) == NULL ) {
		for (;;) {
			njPrint(NJM_LOCATION(3, 5), "can't open %s", fname);
			njWaitVSync();
		}
	}
	gdFsGetFileSize(gdfs, &fsize);
	nsct = (fsize+2047)/2048;
	if ( (dat=syMalloc(nsct*2048)) != NULL ) {
		gdFsRead(gdfs, nsct, dat);
	}
	gdFsClose(gdfs);
	if ( len != NULL )
		*len = fsize;

	return dat;
}

/*	時間への変換															*/
/*	Convert form sampling frequency and counter to time(hour,min,sec,frame)	*/
void sfcnt2time(long sf, long ncnt, long *hh, long *mm, long *ss, long *ff)
{
	register long nf = (long)((double)ncnt/(double)sf*100.0);

	*hh = nf/(60*60*100);
	*mm = nf/(60*100) - *hh*60;
	*ss = nf/100 - *hh*60*60 - *mm*60;
	*ff = nf%100;
}

/***
 *			サンプルプログラム
 *			Sample program
 ***/

/*	ADXファイル再生中にストリームジョイントへファイル読み込むサンプル	*/
/*	Read file to Stream Joint during playing ADX file					*/
void adxsmp(void)
{
	const NJS_PERIPHERAL	*per;
	long					i, txtlen;
	ADXF					adxf;
	ADXT					adxt;
	SJ						sj;
	SJCK					ck, ck2;
	char					*buf, text[256];
	char					*statstr[] = {"INIT   ","STOP   ",
											"READING","READEND"};

	adxt = ADXT_Create(2, work, WKSIZE);
	ADXT_StartFname(adxt, "loopsmp.adx");

	/*	ファイルのオープン	*/
	/*	Open file			*/
	adxf = ADXF_Open("sample.txt", NULL);
	/*	ストリームジョイントの生成	*/
	/*	Create Stream Joint			*/
	buf = syMalloc(2048*10+256);
	sj = SJRBF_Create(buf, 2048*10, 256);

	memset(text, 0, sizeof(text));
	for (;;) {
		per = njGetPeripheral(NJD_PORT_A0);

		/*	ストリームデータ読み込み	*/
		/*	Read data to Stream Joint	*/
		if (per->press & NJD_DGT_TA) {
			ADXF_ReadSj32(adxf, 50, sj);
		}
		if (per->press & NJD_DGT_TB) {
			ADXF_Stop(adxf);
			SJ_Reset(sj);
		}
		if (per->press & NJD_DGT_ST)
			ADXF_Seek(adxf, 0, ADXF_SEEK_SET);

		/*	ストリームジョイントからのデータの取り出し	*/
		/*	Read-out data from Stream Joint				*/
		SJ_GetChunk(sj, SJ_LIN_DATA, 255, &ck);
		/*	改行コードまでのサーチ	*/
		/*	Search paragraph code	*/
		for (i=0; i<ck.len; i++) {
			if ( ck.data[i] == '\n' )
				break;
		}
		if ( i == ck.len )
			txtlen = 0;
		else
			txtlen = i+1;
		memcpy(text, ck.data, txtlen);
		text[txtlen] = 0;
		SJ_SplitChunk(&ck, txtlen, &ck ,&ck2);
		SJ_PutChunk(sj, SJ_LIN_FREE, &ck);
		SJ_UngetChunk(sj, SJ_LIN_DATA, &ck2);

		/*	データの表示	*/
		/*	Display data	*/
		njPrint(NJM_LOCATION(3, 3), "ADX Sample Program No.5");
		njPrint(NJM_LOCATION(3, 4), "   ( ADX File system )");
		njPrint(NJM_LOCATION(3, 6), "%s", statstr[ADXF_GetStat(adxf)]);
		njPrint(NJM_LOCATION(3, 7), "ADXF:Tell=%5d, NumRead=%5d",
								ADXF_Tell(adxf), ADXF_GetNumReadSct(adxf));
		njPrint(NJM_LOCATION(3, 8), "SJ: ndata=%d", 
								SJ_GetNumData(sj, SJ_LIN_DATA));
		njPrint(NJM_LOCATION(3, 9), "%s", text);
		njWaitVSync();
	}
	ADXF_Close(adxf);
	ADXT_Destroy(adxt);
}

/***
 *		メイン処理
 *		MAIN
 ***/

/*	アプリケーション終了処理	*/
/*	Finalize application		*/
void usr_exit(void)
{
	/*	ADXTライブラリ終了処理				*/
	/*	Finalize ADXT library				*/
	ADXT_Finish();
	/*	忍ライブラリの終了処理				*/
	/*	Finalize SHINOBI					*/
	sbExitSystem();
	/*	シンプルプレイヤへジャンプ			*/
	/*	Jump to Simple player				*/
	syBtExit();
}

/*	GDファイルシステムのエラー発生時に起動する関数			*/
/*	Callback function when an error occur in GD file system	*/
void usr_gd_err_func(void *obj, Sint32 errcode)
{
	if (errcode == GDD_ERR_TRAYOPEND || errcode == GDD_ERR_UNITATTENT) {
		/*	アプリケーション終了処理	*/
		/*	Finalize application		*/
		usr_exit();
 	}
 	return;
}

/*	ユーザがV-SYNC割り込みに登録する関数	*/
/*	Callback function when V-sync interrupt	*/
void usr_vsync_func(void)
{
	Sint32 dstat;

    /*	サウンドライブラリのサーバ関数		*/
	/*	Server function of sound library	*/
    sdSysServer();

	/*	GDドライブの状態チェック	*/
	/*	check status of GD-drive	*/
	dstat = gdFsGetDrvStat();
	if (dstat == GDD_DRVSTAT_OPEN || dstat == GDD_DRVSTAT_BUSY) {
		gdFsReqDrvStat();
	}
	return;
}

/*	ADXライブラリのエラー発生時に起動する関数				*/
/*	Callback function when an error occur in ADX library	*/
void usr_adx_err_func(void *obj, char *msg)
{
	/*	
	 *	ADXで、エラーが発生するとこの関数が呼び出されます。
	 *	msg にエラーメッセージが渡されます。
	 *	msgは、R5レジスタに格納されているので、R5レジスタのアドレスを
	 *	ダンプすることによってエラーメッセージを知ることができます。
	 *	この関数は、V-Sync割り込みから呼び出されることがありますので、
	 *	Ninjaの関数は使用しないでください。
	 *	また、アプリケーションをリリースするときは、何もせずに戻るような
 	 *	関数にしてください。
	 *	This function call when error occur in ADX library.
	 *	Error message is put into 'msg' variable.
	 *	'msg' house R5 register. You can look error message in address of 
	 *	R5 register.
	 *	You cannot call Ninja function because this function is called 
	 *	from V-Int.
	 *	You must change this to no operation function when you release 
	 *	the application.
	 */

	for (;;);		/*	リリース時には無くす					*/
					/*	Delete when you release application.	*/

}

void main(void)
{
	void *snddrv;

	/*	忍ライブラリの初期化	*/
	/*	Initialize SHINOBI		*/
    njSetBorderColor( 0x00000000 ) ;
	sbInitSystem(NJD_RESOLUTION_320x240_NTSCNI,NJD_FRAMEBUFFER_MODE_RGB565,1);
	njInitVertexBuffer( -30000, 0, 30000, 0, 0);
    njInitPrint(NULL, 0, 0);
	/*	GDファイルシステムエラーコールバック関数の登録	*/
	/*	Entry callback function of GD file system error	*/
	gdFsEntryErrFuncAll((void *)usr_gd_err_func, NULL);

	/*	サウンドライブラリの準備	*/
	/*	Prepare sound library		*/
	snddrv = load_file("manatee.drv", NULL);
	sound_init(snddrv);
	syFree(snddrv);
	/*	DMA 転送モード				*/
	/*	Set transfer mode to DMA  	*/
	sdMemBlkSetTransferMode(SDE_MEMBLK_TRANSFER_MODE_DMA);

	njSetVSyncFunction((void (*)(void))usr_vsync_func);

	/*	ADXライブラリ初期化		*/
	/*	Initialize ADX library	*/
	ADXT_Init();
	/*	エラーコールバック関数の登録		*/
	/*	Entry ADX error callback function	*/
	ADXT_EntryErrFunc(usr_adx_err_func, NULL);

	for (;;)
		adxsmp();

	/*	アプリケーション終了処理	*/
	/*	Finalize application		*/
	usr_exit();
}
