/*	
 *	clipgym.h
 *
 *	P/ECE GYM Driver
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Thu Dec 9 18:16:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#ifndef __CLIP_GYM_H__
#define __CLIP_GYM_H__

/****************************************************************************
 *	GYMファイル仕様
 ****************************************************************************/

/*
 *	GYMファイルヘッダ
 */
typedef struct _GYMHEADER {
	char identifier[4];			/* +  0,  4: Identifier: the characters 'G' 'Y' 'M' 'X' */
	char song_title[32];			/* +  4, 32: Song title */
	char game_title[32];			/* + 36, 32: Game title */
	char game_publisher[32];		/* + 68, 32: Game publisher */
	char name_of_emulator[32];		/* +100, 32: Name of emulator used to create the gym file */
	char name_of_person[32];		/* +132, 32: Name of person who created the gym file */
	char miscelaneous_comments[256];	/* +164,256: Miscelaneous comments */
	int  loop_integer;			/* +420,  4: Loop integer (unsigned little endian). */
						/*           Zero if the gym should not loop. */
						/*           Otherwise it specifies the point (1 being the very beginning) at which looping begins in 1/60 seconds. */
	int  uncompressed_size;			/* +424,  4: Specifies the uncompressed size (unsigned little endian) of the raw gym data if the gym data has been compressed with zlib (deflate algorythm). */
						/*           Zero if not compressed. */
} GYMHEADER;					/* =428 */
// このあとにraw gym dataが続きます。

/****************************************************************************
 *	GYMドライバ
 ****************************************************************************/

#define GYMFRAME	3	/* datbuf[]読み込み時に時間がかかるので、何フレームかまとめて処理する必要あり。 */
				/* そうしないと、datbuf[]を読み込んだときだけ処理が送れて音が飛んでしまう。     */
				/* 何フレームまとめて処理するかは調整可能ですが、最低2フレーム以上は必要です。  */
				/* ただし、あまり大きくしすぎるとスタック溢れが発生するようです。               */
				/* 割り込みが多重にかかりすぎるため？原因不明、要調査です。                     */
				/* →解決しました。ストリーム再生の多重割り込み過多が原因でした。               */
				/*   clippce.cのstream_isr変更に関するコメント(2004/12/12)を参照してください。  */
#define GYMBUFLEN	320	/* =16000/50 … 1[tick](=1/50[sec])当たりのサンプル数 */
#define GYMBUFLEN_60	267	/* =16000/60 … 1/60[sec]当たりのサンプル数 (GYM単位) */
#define GYMBUFLEN_DAC	GYMBUFLEN_60 /* 1/60[sec]分のDAC出力サンプル数上限 */

/*
 *	GYMドライバ
 */
typedef struct _GYMDRIVER {
	YM2612* ym2612;					/* YM2612  chip (高速RAMに配置) */
	SN76489* sn76489;				/* SN76489 chip (   SRAMに配置) (本当はSN76496です。同等品？) */

	int loopcnt;					/* ループ開始位置までの残りフレーム数 (loop_integer->-∞へのダウンカウンタ)                          */
							/* loop_integerが0ならば非ループ曲です。非ループ曲とループ曲の違いは、次のように処理しています。     */
							/*   1. 初期状態で loopcnt=loop_integer, looppos=0 とする。                                          */
							/*   2. 0(1フレーム経過)コマンドを読むたびに、loopcntを1減らす。                                     */
							/*   3. loopcnt=0になったら、現在の位置(=ファイル位置-バッファ内未処理データ数)をloopposに格納する。 */
							/*   4. ファイル終端に達し、かつ、loopposが0でなければ、looppos位置へシークする。                    */
							/* 二周目以降も、loopcntは-1→-∞へ変化し続けますが、再び0になることはまず無いので大丈夫です。       */
							/* (このような処理方法にした理由は、少しでもコードを小さくして、FRAM配置の可能性を上げるためです)    */
							/* また、loop_integerが0(非ループ曲)ならば、最初の変化で0->-1となり、0になる瞬間が検出されないので、 */
							/* loopposが設定されることはなく0のままを維持し、非ループの挙動が実現できます。                      */
	int looppos;					/* ループ開始ファイル位置。0ならばループ位置未検出。詳しくは上のコメントを参照してください。         */

	FILE* fp;					/* ファイルポインタ */
	int datlen;					/* datbufの有効バイト数 */
	int datpos;					/* datbufの走査位置 */
	unsigned char datbuf[BLOCKSIZE + 3];		/* ファイル読み込みバッファ */

	int tmpcnt;					/* tmpbufの残りサンプル数 */
	short tmpbuf[3][GYMBUFLEN + GYMBUFLEN_60];	/* 1/60→1/50変換用の一時バッファ ([0]=YM2612, [1]=SN76489, [2]=DAC) */
} GYMDRIVER;

/*
 *	GYMドライバ内部関数
 */
int gym_init(GYMDRIVER* driver, const char* fname);
void gym_free(GYMDRIVER* driver);
int gym_mix(GYMDRIVER* driver, short wbuff[/*GYMBUFLEN*/]);
int gym_stream_callback(short* wbuff, int param);
void gym_dacmix(short dst[/*GYMBUFLEN_60*/], const short src[/*daccnt*/], int daccnt);
void gym_copy(const short src1[/*GYMBUFLEN*/], const short src2[/*GYMBUFLEN*/], const short src3[/*GYMBUFLEN*/], short dst[/*GYMBUFLEN*/]);

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

/* GYMファイルの再生を開始します。
 * [in]
 *	fname		GYMファイル名。
 * [out]
 *	戻り値		再生開始に成功したら、 0を返します。
 *			再生開始できなければ、-1を返します。
 * [note]
 *	* この関数は内部でstream_play()を呼び出しています。
 *	* stream_play()を呼ぶと、サウンド割り込みベクタがフックされます。
 *	  プログラム終了前に必ずgym_stop()を呼んで、ベクタを復元してください。
 */
int gym_play(const char* fname);

/* GYMファイルの再生を停止します。
 * [note]
 *	* 再生中でなければ、この関数は何もしません。
 */
void gym_stop();

#endif /*__CLIP_GYM_H__*/
