/*	
 *	clipmod.c
 *
 *	P/ECE MOD Driver
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2017 Naoyuki Sawa
 *
 *	* Sun Sep 14 18:44:00 JST 2003 Naoyuki Sawa
 *	- clippce.cから分割。
 *	* Wed Aug 16 22:59:27 JST 2017 Naoyuki Sawa
 *	- 64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
 */
#include "clip.h"

/****************************************************************************
 *	P/ECE MOD Driver/Stream
 ****************************************************************************/

/*---------- P/ECE MOD Driver (マシン非依存部分) ----------*/

typedef struct _MODTAG {
	char tag[4 + 1];	/* タグ文字列 */
	unsigned char chans;	/* チャネル数 */
} MODTAG;
static const MODTAG mod_tags[] = {
	{ "M.K.",  4 },		/* これが標準MOD形式 */
	{ "1CHN",  1 },		/* 以下、拡張MOD形式 */
	{ "2CHN",  2 },
	{ "3CHN",  3 },
	{ "4CHN",  4 },
	{ "5CHN",  5 },
	{ "6CHN",  6 },
	{ "7CHN",  7 },
	{ "8CHN",  8 },
	{ "9CHN",  9 },
	{ "10CH", 10 },
	{ "11CH", 11 },
	{ "12CH", 12 },
	{ "13CH", 13 },
	{ "14CH", 14 },
	{ "15CH", 15 },
	{ "16CH", 16 },
	{ "    ",  0 },		/* (終端) */
};

int
mod_init(MODDRIVER* driver, const void* data)
{
	MODHEADER* header = (MODHEADER*)data;
	int i, patno_max;
	MODSAMPLE* sample;
	unsigned int* pattern;
	unsigned char* smpdat;

	/* まずゼロクリア。 */
	memset(driver, 0, sizeof(MODDRIVER));

	/* ヘッダアドレスを格納します。 */
	driver->header = header;

	/* タグを調べて、チャネル数を格納します。 */
	for(i = 0; mod_tags[i].chans != 0; i++) {
		if(memcmp(header->tag, mod_tags[i].tag, 4) == 0) break;
	}
	if(mod_tags[i].chans == 0) return -1; /* 未知のタグ */
	driver->chans = mod_tags[i].chans;

	/* 最大パターン番号を調べます。 */
	patno_max = -1;
	for(i = 0; i < header->seqlen; i++) {
		if(header->sequence[i] > patno_max) {
			patno_max = header->sequence[i];
		}
	}

	/* パターンアドレスを格納します。 */
	pattern = (unsigned int*)(header + 1);
	for(i = 0; i <= patno_max; i++) {
		driver->pattern[i] = pattern;
		pattern += driver->chans * 64; /* ROW=NOTE*chans, PATTERN=ROW*64 */
	}

	/* サンプルデータアドレスを格納します。 */
	smpdat = (unsigned char*)pattern;
	for(i = 0, sample = header->sample; i < 31; i++, sample++) {
		driver->smpdat[i] = smpdat;
		smpdat += SWAPH(sample->length) * 2;
	}

	/* 初期値設定。 */
	driver->speed = 6;
	driver->tempo = 125;
	driver->tick = (driver->speed - 1) * driver->tempo; /* 初回タイムアウトするように */

	return 0;
}

int
mod_tick(MODDRIVER* driver, short wbuff[/*MODBUFLEN*/])
{
	int retval;

	/* Mon Feb 24 19:00:00 JST 2003 Naoyuki Sawa
	 * - エフェクトF00(SetSpeed=0)が指定されたら、演奏停止します。
	 *   FastTracker 2も、このような挙動になっているそうです。
	 * - 本来、MODHEADER.seqrepに127以上の値を指定すれば、曲はループしなくなります。
	 *   しかしModPlugTrackerでは、seqrepに有効なシーケンス番号より大きな値を指定できないようで、
	 *   127を指定しても、セーブすると値が変わってしまい、ループ曲扱いになってしまいます。
	 *   曲の最後に発声なしのパターンを置いて、そこだけでループさせて対処することもできますが、
	 *   余計に1パターン分の容量（4トラック時で1KB）を消費してしまいます。
	 *   そこで、曲の最後にSetSpeed=0を置いて、演奏を停止する方法を採ることにしました。
	 *   これはMODの正式な仕様ではありませんが、FastTracker 2互換ならばさほど問題はないでしょう。
	 * - これまでどおり、seqrepに127以上の値をして曲を停止する方法も有効です。
	 *   それが正しい方法なので、トラッカーが対応しているなら、seqrepによる停止方法を使ってください。
	 */
	if(driver->speed == 0) return 1;

	/* シーケンス処理。 */
	retval = mod_seq(driver);
	if(retval != 0) return retval;

	/* ミキシング処理。 */
	retval = mod_mix(driver, wbuff);
	if(retval != 0) return retval;

	return 0;
}

/*---------- P/ECE MOD Stream (マシン依存部分) ----------*/

static int
//{{2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
//mod_stream_callback(short* wbuff, int param)
//↓2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
mod_stream_callback(short* wbuff, intptr_t param)
//}}2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
{
	return mod_tick((MODDRIVER*)param, wbuff);
}

int
mod_play(const void* data)
{
	static MODDRIVER driver; /* ＳＴＡＴＩＣです！ */

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

	/* MODドライバを初期化します。 */
	if(mod_init(&driver, data) != 0) return -1;

	/* ストリーム再生を開始します。 */
//{{2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
//	stream_play(MODBUFLEN, mod_stream_callback, (int)&driver, 0);
//↓2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。
	stream_play(MODBUFLEN, mod_stream_callback, (intptr_t)&driver, 0);
//}}2017/08/16変更:64ビット対応のために、STREAMCALLBACKのparamの型を、int⇒intptr_tに変更しました。

	return 0;
}

void
mod_stop()
{
	/* ストリーム再生を停止します。 */
	stream_stop();

	/* MODドライバのクリーンアップは不要です。 */
}

