#include <clip.h>

/****************************************************************************/

#define MAXCH	4		/* チャネル数                 */
#define BLKN	4		/* ブロック数                 */
#define BLKS	128/*64*/	/* ブロックサイズ(サンプル数) */

typedef struct _STREAM {
	int active;		/* 0:停止中/1:再生中 */
	/* ファイル関連 */
	FILEACC fa;		/* GSMファイル               */
	int ofs;		/* GSMファイルの読み込み位置 */
	/* GSM関連 */
	gsm g;			/* GSMコンバータ             */
	gsm_signal s[160];	/* 展開済みGSMデータバッファ */
	int rest;		/* GSMデータの残りサンプル数 */
	/* Wave関連 */
	int ch;			/* 再生チャネル */
	PCEWAVEINFO wi[BLKN];	/* WAVEヘッダ   */
	short wb[BLKN][BLKS];	/* WAVEデータ   */
} STREAM;
static STREAM stream;

int stream_play(int ch, const char* fname);
void stream_stop();
static void stream_endproc(PCEWAVEINFO* wi);
static void stream_fill(short* data);

int
stream_play(int ch, const char* fname)
{
	STREAM* ps = &stream;

	int retval, i;
	PCEWAVEINFO* wi;

	/* 確実に停止します。 */
	stream_stop();

	/* 全体をクリアします。 */
	memset(ps, 0, sizeof(STREAM));

	/* ファイルを開きます。 */
	retval = pceFileOpen(&ps->fa, fname, FOMD_RD);
	if(retval != 0) {
		return -1;
	}

	/* GSMコンバータを作成します。 */
	ps->g = gsm_create();
	if(ps->g == NULL) {
		pceFileClose(&ps->fa);
		return -1;
	}

	/* 再生チャネルを格納します。 */
	if(ch < 0 || MAXCH - 1 < ch) {
		gsm_destroy(ps->g);
		pceFileClose(&ps->fa);
		return -1;
	}
	ps->ch = ch;

	/* WAVE情報を初期化します。 */
	for(i = 0, wi = ps->wi; i < BLKN; i++, wi++) {
		wi->type = PW_TYPE_16BITPCM; /* 輪にするのでPW_TYPE_CONT不要 */
		wi->pData = &ps->wb[i][0];
		wi->len = BLKS;
		wi->next = &ps->wi[(i + 1) % BLKN]; /* 輪にします */
		wi->pfEndProc = stream_endproc;
		stream_fill(wi->pData); /* バッファを満たします。 */
	}
}

void
stream_stop()
{
	STREAM* ps = &stream;

	/* 再生中でなければ何もしません。 */
	if(!ps->active) return;

	/* WAVE再生を停止します。 */
	pceWaveAbort(ps->ch);

	/* GSMコンバータを削除します。 */
	gsm_destroy(ps->g);

	/* ファイルを閉じます。 */
	pceFileClose(&ps->fa);
}

static void
stream_endproc(PCEWAVEINFO* wi)
{
	/* 新しいデータでバッファを満たします。 */
	stream_fill(wi->pData);
}

static void
stream_fill(short* data)
{
	/* TODO: */
}

/****************************************************************************/

unsigned char vbuff[DISP_X * DISP_Y];

int
main()
{
	/* 一般的な初期化。 */
	pceLCDDispStart();
	pceAppSetProcPeriod(50);
	pceLCDSetBuffer(vbuff);

	if(stream_play("sample.gsm") == 0) {
		yield();
		while(pcePadGetDirect() == 0)
			;
		stream_stop();
	}

	return 0;
}

#if 0
/****************************************************************************/

#define BLKN		4		/* ブロック数         */
#define BLKS		128		/* Waveブロックサイズ */

static FILEACC fa;			/* GSMファイルアクセス用構造体 */
static int ofs;				/* 次回の読み込みオフセット    */

static gsm g;				/* GSMストリーム           */
static gsm_frame gf;			/* GSMフレーム(圧縮形式)   */
static gsm_signal gs[160];		/* GSMフレーム(非圧縮形式) */
static int gs_rest;

static PCEWAVEINFO wi[BLKN];		/* Waveヘッダ */
static signed short wb[BLKN][BLKS];	/* Waveデータ */

static volatile unsigned char busy;	/* GSMストリーム再入防止用  */
static volatile unsigned char blkip;	/* 再生中のブロック番号     */
static volatile unsigned char blkop;	/* 次回の展開先ブロック番号 */

static void endproc(PCEWAVEINFO* pwi/*この値は使わない*/);

int
gsm_play(const char* fname)
{
	int retval, i;
	PCEWAVEINFO* pwi;

	/* 確実に停止します。 */
	gsm_stop();

	/* GSMファイルを開きます。 */
	retval = pceFileOpen(&fa, fname, FOMD_RD);
	if(retval != 0) {
		return -1;
	}
	ofs = 0;
	gs_rest = 0;

	/* GSMストリームを作成します。 */
	g = gsm_create();
	if(g == NULL) {
		pceFileClose(&fa);
		return -1;
	}

	/* Waveヘッダを初期化します。 */
	memset(wi, 0, sizeof wi);
	for(i = 0; i < BLKN; i++) {
		pwi = &wi[i];
		pwi->type = PW_TYPE_16BITPCM;
		pwi->pData = &wb[i][0];
		pwi->len = BLKS;
		pwi->next = &wi[(i + 1) % BLKN]; /* 輪になってます */
		pwi->pfEndProc = endproc;
	}

	/* 最初のデータフィル。 */
	busy  = 0;
	blkip = 0;
	blkop = BLKN - 1; /* 最終ブロックの完了通知と見なす */
	endproc(NULL);

	/* 再生開始。 */
	pceWaveDataOut(0, &wi[0]);

	return 0;
}

void
gsm_stop()
{
	/* 再生停止。 */
	pceWaveAbort(0);

	/* GSMストリームを削除します。 */
	if(g != NULL) {
		gsm_destroy(g);
		g = NULL;
	}

	/* ファイルを閉じます。 */
	if(fa.valid) {
		pceFileClose(&fa);
	}
}

static void
endproc(PCEWAVEINFO* pwi/*この値は使わない*/)
{
	int retval, i, j;
	unsigned char b;
	short s, *ps, *pd;

	/* 再入防止。 */
DISABLE;
	/* 再生中のブロック番号は必ず進めます。 */
	if(++blkop == BLKN) blkop = 0;
	b = busy;
	busy = 1;
ENABLE;
	if(b) return;

	do {
		/* 展開先ブロックのWaveヘッダを取得します。 */
		pwi = &wi[blkip];
		pd = (short*)pwi->pData;

		/* GSMデータ読み込み。 */
		retval = file_read(&fa, gf, ofs, sizeof gf);
		if(retval != sizeof gf) {
			pceWaveAbort(0);
			goto EXIT;
		}
		ofs += retval;

		/* GSMデータ展開。 */
		retval = gsm_decode(g, gf, gs);
		if(retval != 0) {
			pceWaveAbort(0);
			goto EXIT;
		}

		/* 8KHz->16KHz変換。 */
		ps = gs;
		for(j = 0; j < BLKG; j++) {
			s = *ps++;
			*pd++ = s;
			*pd++ = s;
		}

		/* 次回の展開先ブロック番号を進めます。 */
		if(++blkip == BLKN) blkip = 0;
	} while(blkip != blkop); /* 再生中のブロックの一つ手前まで展開 */

EXIT:
	/* これ忘れないように！ */
	busy = 0;
}
#endif
