/* ================================================
     zmd2mid bend,ARCC,modulationをセットする関数
	一次的な最適化も行う(同じ値は繰り返しセットしない)
   ================================================ */

#include "Putcode.h"
#include "VarLen.h"

#include "config.h"
void ctrlchg_init(void);
void bendchg_init(BYTE type);
int bendrange(UBYTE *MTrk,DWORD *gate_old,BYTE range,DWORD trk);	/* ベンドレンジ変更 */
int bendchg(UBYTE *MTrk,DWORD *gate_old,WORD bend,
		WORD port_now,WORD special_tie,WORD extMOD_now,DWORD trk);	/* ベンド変更 */
int ARCCchg(UBYTE *MTrk,DWORD *gate_old,UBYTE ARCCc,BYTE ARCCv,DWORD trk);	/* ARCC変更 */
int MODchg(UBYTE *MTrk,DWORD *gate_old,WORD Mdepth,DWORD trk);	/* modulation変更 */
int volchg(UBYTE *MTrk,DWORD *gate_old,WORD vol,DWORD trk);		/* volume変更 */
int volchk(void);
int put_ex(UBYTE *MTrk,DWORD *gate_old,UBYTE ID[4],
		UBYTE *data,DWORD bytes,BYTE checksum_type,BYTE runstatus);


static WORD bend_now,Mdepth_now;
static UWORD vol_now;				/* 各種最終設定値 */
static BYTE ARCC_now,OPM = 0;

void ctrlchg_init(void)
{
	bend_now = 0x8000;			/* 現在の楽器のbend設定値 初期化 */
	ARCC_now = -128;			/* 現在の楽器のARCC設定値 初期化 */
	Mdepth_now = 0x8000;			/* 現在の楽器のmodulation設定値 初期化 */
	vol_now = 0x8000;			/* 現在のvolume 初期化 */
}


void bendchg_init(BYTE type)
{
	OPM = type;
}



int bendrange(UBYTE *MTrk,DWORD *gate_old,BYTE range,DWORD trk)	/* ベンドレンジ変更 */
{
	UBYTE *MTrk_ = MTrk;

	MTrk += Putcode(MTrk,*gate_old,0xB0+trk,0x64,0x00);
	MTrk += Putcode(MTrk,0,0xB0+trk,0x65,0x00);
	MTrk += Putcode(MTrk,0,0xB0+trk,0x06,range);
	*gate_old = 0;

	return MTrk - MTrk_;
}


int bendchg(UBYTE *MTrk,DWORD *gate_old,WORD bend,
		WORD port_now,WORD special_tie,WORD extMOD_now,DWORD trk)	/* ベンド変更 */
{
	UBYTE *MTrk_ = MTrk;

	bend += port_now;
	bend += special_tie;
	bend += extMOD_now;
	if (bend >= 8192) {					/* 安全装置(port.などでは必ず引っ掛かる) */
		bend =  8191;
	} else if (bend< -8192) {
		bend = -8192;
	}
	if (bend != bend_now) {
		UBYTE val;

		bend_now = bend;
		if (OPM) {
			bend = bend * 683 / 64;
		}
		bend += 0x2000;
		val  = bend & 0x7F;	/* */
		bend >>= 7;			/* */
		MTrk += Putcode(MTrk,*gate_old,0xE0 + trk,val,bend);
		*gate_old = 0;
	}
	return MTrk - MTrk_;
}


int ARCCchg(UBYTE *MTrk,DWORD *gate_old,UBYTE ARCCc,BYTE ARCCv,DWORD trk)
{
	UBYTE *MTrk_ = MTrk;

	if (ARCCv != ARCC_now) {
		ARCC_now = ARCCv;
		MTrk += Putcode(MTrk,*gate_old,0xB0+trk,ARCCc,ARCCv);
		*gate_old = 0;
	}
	return MTrk - MTrk_;
}


int MODchg(UBYTE *MTrk,DWORD *gate_old,WORD Mdepth,DWORD trk)
{
	UBYTE *MTrk_ = MTrk;

	if (Mdepth > 127) {
		Mdepth = 127;
	} else if (Mdepth < 0  ) {
		Mdepth = 0;
	}
	if (Mdepth != Mdepth_now) {
		Mdepth_now = Mdepth;
		MTrk += Putcode(MTrk,*gate_old,0xB0+trk,0x01,Mdepth);
		*gate_old = 0;
	}
	return MTrk - MTrk_;
}


int volchg(UBYTE *MTrk,DWORD *gate_old,WORD vol,DWORD trk)		/* volume変更 */
{
	UBYTE *MTrk_ = MTrk;

	if (vol != vol_now) {
		vol_now = vol;
		MTrk += Putcode(MTrk,*gate_old,0xB0+trk,0x07,vol);
		*gate_old = 0;
	}

	return MTrk - MTrk_;
}


inline int volchk(void)
{
	return ( vol_now != 0x8000 )? 1:0;
}


int put_ex(UBYTE *MTrk,DWORD *gate_old,UBYTE ID[4],
		UBYTE *data,DWORD bytes,BYTE checksum_type,BYTE runstatus)
{
	/* exclusive を出力する。
	 * switch checksum_type
	 *   case 0: MIDIの生データ。ベタで書く。ID[]も無視する。
	 * 			bytesには *dataのバイト数を。
	 *        1: Roland Sumをつけて出力。dataにはf0,f7,sumを含まず、
	 *			bytesにも勘定しないこと。
	 *        2: Roland sumと、IDの4byte目を付けない以外は1と同じ。
	 */

	UBYTE *MTrk_ = MTrk;
	DWORD i;
	UBYTE sum = 0;

	MTrk += PutVarLen(MTrk,*gate_old);		/* gate time */
	*gate_old = 0;
	if (checksum_type != 0) {
		const int idbyte = (checksum_type == 1)? 4 : 3;
		const int sumbyte = (checksum_type == 1)? 1 : 0;
		*MTrk++ = 0xF0;				/* F0 */
		MTrk += PutVarLen(MTrk,idbyte+bytes+sumbyte+1);	/* ID+bytes+sum+F7 */
		for (i = 0; i < idbyte; i++) {		/* ID*4 */
			*MTrk++ = ID[i];
		}
	}
	for (i = 0; i < bytes; i++) {		/* data */
		sum -= *MTrk++ = *data++;
	}
	if (checksum_type == 1) {
		sum  &= 0x7F;
		*MTrk++ = sum;
	}
	if (checksum_type != 0) {
		*MTrk++ = 0xF7;
	}
	status_init(runstatus == 1? USERUNSTAT : NOUSERUNSTAT );

	return MTrk - MTrk_;
}
