// ---------------------------------------------------------------------------
//	FM Sound Generator - Core Unit
//	Copyright (C) cisc 1998, 2003.
// ---------------------------------------------------------------------------
//	$Id: fmgen.cpp,v 1.50 2003/09/10 13:19:34 cisc Exp $
// ---------------------------------------------------------------------------
//	参考:
//		FM sound generator for M.A.M.E., written by Tatsuyuki Satoh.
//
// 	謎:
//		OPNB の CSM モード(仕様がよくわからない)
//
//	制限:
//		・AR!=31 で SSGEC を使うと波形が実際と異なる可能性あり
//
//	謝辞:
//		Tatsuyuki Satoh さん(fm.c)
//		Hiromitsu Shioya さん(ADPCM-A)
//		DMP-SOFT. さん(OPNB)
//		KAJA さん(test program)
//		ほか掲示板等で様々なご助言，ご支援をお寄せいただいた皆様に
// ---------------------------------------------------------------------------

#include "headers.h"
#include "misc.h"
#include "fmgen.h"
#include "fmgeninl.h"

#include "../../fileio.h"


#include "../../statesub.h"

#define LOGNAME "fmgen"

// ---------------------------------------------------------------------------

#define FM_EG_BOTTOM 955

// ---------------------------------------------------------------------------
//	Table/etc
//
namespace FM
{
	const uint8 Operator::notetable[128] =
	{
		 0,  0,  0,  0,  0,  0,  0,  1,  2,  3,  3,  3,  3,  3,  3,  3, 
		 4,  4,  4,  4,  4,  4,  4,  5,  6,  7,  7,  7,  7,  7,  7,  7, 
		 8,  8,  8,  8,  8,  8,  8,  9, 10, 11, 11, 11, 11, 11, 11, 11, 
		12, 12, 12, 12, 12, 12, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 
		16, 16, 16, 16, 16, 16, 16, 17, 18, 19, 19, 19, 19, 19, 19, 19, 
		20, 20, 20, 20, 20, 20, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23, 
		24, 24, 24, 24, 24, 24, 24, 25, 26, 27, 27, 27, 27, 27, 27, 27, 
		28, 28, 28, 28, 28, 28, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31, 
	};
	
	const int8 Operator::dttable[256] =
	{
		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
		  0,  0,  0,  0,  2,  2,  2,  2,  2,  2,  2,  2,  4,  4,  4,  4,
		  4,  6,  6,  6,  8,  8,  8, 10, 10, 12, 12, 14, 16, 16, 16, 16,
		  2,  2,  2,  2,  4,  4,  4,  4,  4,  6,  6,  6,  8,  8,  8, 10,
		 10, 12, 12, 14, 16, 16, 18, 20, 22, 24, 26, 28, 32, 32, 32, 32,
		  4,  4,  4,  4,  4,  6,  6,  6,  8,  8,  8, 10, 10, 12, 12, 14,
		 16, 16, 18, 20, 22, 24, 26, 28, 32, 34, 38, 40, 44, 44, 44, 44,
		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
		  0,  0,  0,  0, -2, -2, -2, -2, -2, -2, -2, -2, -4, -4, -4, -4,
		 -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14,-16,-16,-16,-16,
		 -2, -2, -2, -2, -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10,
		-10,-12,-12,-14,-16,-16,-18,-20,-22,-24,-26,-28,-32,-32,-32,-32,
		 -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14,
		-16,-16,-18,-20,-22,-24,-26,-28,-32,-34,-38,-40,-44,-44,-44,-44,
	};

	const int8 Operator::decaytable1[64][8] = 
	{
		0, 0, 0, 0, 0, 0, 0, 0,		0, 0, 0, 0, 0, 0, 0, 0,
		1, 1, 1, 1, 1, 1, 1, 1,		1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1,		1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 0, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 1, 0, 1, 0, 1, 0,		1, 1, 1, 0, 1, 0, 1, 0,
		1, 1, 1, 0, 1, 1, 1, 0,		1, 1, 1, 1, 1, 1, 1, 0,
		1, 1, 1, 1, 1, 1, 1, 1,		2, 1, 1, 1, 2, 1, 1, 1,
		2, 1, 2, 1, 2, 1, 2, 1,		2, 2, 2, 1, 2, 2, 2, 1,
		2, 2, 2, 2, 2, 2, 2, 2,		4, 2, 2, 2, 4, 2, 2, 2,	
		4, 2, 4, 2, 4, 2, 4, 2,		4, 4, 4, 2, 4, 4, 4, 2,
		4, 4, 4, 4, 4, 4, 4, 4,		8, 4, 4, 4, 8, 4, 4, 4,	
		8, 4, 8, 4, 8, 4, 8, 4,		8, 8, 8, 4, 8, 8, 8, 4,
		16,16,16,16,16,16,16,16,	16,16,16,16,16,16,16,16,
		16,16,16,16,16,16,16,16,	16,16,16,16,16,16,16,16,
	};

	const int Operator::decaytable2[16] = 
	{
		1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2047, 2047, 2047, 2047, 2047
	};

	const int8 Operator::attacktable[64][8] = 
	{
		-1,-1,-1,-1,-1,-1,-1,-1,	-1,-1,-1,-1,-1,-1,-1,-1,
		 4, 4, 4, 4, 4, 4, 4, 4,	 4, 4, 4, 4, 4, 4, 4, 4,
		 4, 4, 4, 4, 4, 4, 4, 4,	 4, 4, 4, 4, 4, 4, 4, 4,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4,-1, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4,-1, 4,-1, 4,-1, 4,-1,	 4, 4, 4,-1, 4,-1, 4,-1,
		 4, 4, 4,-1, 4, 4, 4,-1,	 4, 4, 4, 4, 4, 4, 4,-1,
		 4, 4, 4, 4, 4, 4, 4, 4,	 3, 4, 4, 4, 3, 4, 4, 4,
		 3, 4, 3, 4, 3, 4, 3, 4,	 3, 3, 3, 4, 3, 3, 3, 4,
		 3, 3, 3, 3, 3, 3, 3, 3,	 2, 3, 3, 3, 2, 3, 3, 3,
		 2, 3, 2, 3, 2, 3, 2, 3,	 2, 2, 2, 3, 2, 2, 2, 3,
		 2, 2, 2, 2, 2, 2, 2, 2,	 1, 2, 2, 2, 1, 2, 2, 2,
		 1, 2, 1, 2, 1, 2, 1, 2,	 1, 1, 1, 2, 1, 1, 1, 2,
		 0, 0, 0, 0, 0, 0, 0, 0,	 0, 0 ,0, 0, 0, 0, 0, 0,
		 0, 0, 0, 0, 0, 0, 0, 0,	 0, 0 ,0, 0, 0, 0, 0, 0,
	};

	const int Operator::ssgenvtable[8][2][3][2] =
	{
		1, 1,  1, 1,  1, 1,		// 08 
		0, 1,  1, 1,  1, 1,		// 08 56~
		0, 1,  2, 0,  2, 0,		// 09
		0, 1,  2, 0,  2, 0,		// 09
		1,-1,  0, 1,  1,-1,		// 10
		0, 1,  1,-1,  0, 1,		// 10 60~
		1,-1,  0, 0,  0, 0,		// 11
		0, 1,  0, 0,  0, 0,		// 11 60~
		2,-1,  2,-1,  2,-1,		// 12
		1,-1,  2,-1,  2,-1,		// 12 56~
		1,-1,  0, 0,  0, 0,		// 13
		1,-1,  0, 0,  0, 0,		// 13
		0, 1,  1,-1,  0, 1,		// 14
		1,-1,  0, 1,  1,-1,		// 14 60~
		0, 1,  2, 0,  2, 0,		// 15
		1,-1,  2, 0,  2, 0,		// 15 60~
	};

	// fixed equasion-based tables
	int		pmtable[2][8][FM_LFOENTS];
	uint	volatile  amtable[2][4][FM_LFOENTS];

	static bool tablemade = false;

	const char *get_libfmgen_version(void)
	{
#if defined(__LIBFMGEN_VERSION)
		return (const char *)__LIBFMGEN_VERSION;
#else
		return (const char *)"\0";
#endif
	}
}

namespace FM
{

// ---------------------------------------------------------------------------
//	テーブル作成
//
void MakeLFOTable()
{
	if (tablemade)
		return;

	tablemade = true;
	
	int i;

	static const double pms[2][8] = 
	{ 
		{ 0, 1/360., 2/360., 3/360.,  4/360.,  6/360., 12/360.,  24/360., },	// OPNA
//		{ 0, 1/240., 2/240., 4/240., 10/240., 20/240., 80/240., 140/240., },	// OPM
		{ 0, 1/480., 2/480., 4/480., 10/480., 20/480., 80/480., 140/480., },	// OPM
//		{ 0, 1/960., 2/960., 4/960., 10/960., 20/960., 80/960., 140/960., },	// OPM
	};
	//		 3		 6,      12      30       60       240      420		/ 720
	//	1.000963
	//	lfofref[level * max * wave];
	//	pre = lfofref[level][pms * wave >> 8];
	static const volatile uint8 amt[2][4] = 
	{
		{ 31, 6, 4, 3 }, // OPNA
		{ 31, 2, 1, 0 }, //	OPM
	};
	
	for (int type = 0; type < 2; type++)
	{
		for (i=0; i<8; i++)
		{
			double pmb = pms[type][i];
			for (int j=0; j<FM_LFOENTS; j++)
			{
//				double v = pow(2.0, pmb * (2 * j - FM_LFOENTS+1) / (FM_LFOENTS-1));
				double w = 0.6 * pmb * sin(2 * j * 3.14159265358979323846 / FM_LFOENTS) + 1;
//				pmtable[type][i][j] = int(0x10000 * (v - 1));
//				if (type == 0)
					pmtable[type][i][j] = int(0x10000 * (w - 1));
//				else
//					pmtable[type][i][j] = int(0x10000 * (v - 1));

//				printf("pmtable[%d][%d][%.2x] = %5d  %7.5f %7.5f\n", type, i, j, pmtable[type][i][j], v, w);
			}
		}
		for (i=0; i<4; i++)
		{
			for (int j=0; j<FM_LFOENTS; j++)
			{
				amtable[type][i][j] = (((j * 4) >> amt[type][i]) * 2) << 2;
			}
		}
	}
}


// ---------------------------------------------------------------------------
//	チップ内で共通な部分
//
Chip::Chip()
: ratio_(0), aml_(0), pml_(0), pmv_(0), optype_(typeN)
{
	static int __num = 0;
	chip_num = __num++;
}

//	クロック・サンプリングレート比に依存するテーブルを作成
void Chip::SetRatio(uint ratio)
{
	if (ratio_ != ratio)
	{
		ratio_ = ratio;
		MakeTable();
	}
}

void Chip::MakeTable()
{
	int h, l;
	
	// PG Part
	static const float dt2lv[4] = { 1.f, 1.414f, 1.581f, 1.732f };
	for (h=0; h<4; h++)
	{
		assert(2 + FM_RATIOBITS - FM_PGBITS >= 0);
		double rr = dt2lv[h] * double(ratio_);
		for (l=0; l<16; l++)
		{
			int mul = l ? l * 2 : 1;
			multable_[h][l] = uint(mul * rr);
		}
	}
}

// ---------------------------------------------------------------------------
//	ステートセーブ
//
#define CHIP_STATE_VERSION	2

void Chip::DeclState(void *f)
{
	p_logger = (CSP_Logger *)f;
	state_entry = new csp_state_utils(CHIP_STATE_VERSION, chip_num, _T("FMGEN::Chip::"), p_logger);

	DECL_STATE_ENTRY_UINT32(ratio_);
	DECL_STATE_ENTRY_UINT32(aml_);
	DECL_STATE_ENTRY_UINT32(pml_);
	DECL_STATE_ENTRY_INT32(pmv_);
}

void Chip::SaveState(void *f)
{
	FILEIO *state_fio = (FILEIO *)f;

	if(state_entry != NULL) {
		state_entry->save_state(state_fio);
	}
//	state_fio->FputUint32_BE(CHIP_STATE_VERSION);
	
//	state_fio->FputUint32_BE(ratio_);
//	state_fio->FputUint32_BE(aml_);
//	state_fio->FputUint32_BE(pml_);
//	state_fio->FputInt32_BE(pmv_);
}

bool Chip::LoadState(void *f)
{

	FILEIO *state_fio = (FILEIO *)f;
	bool mb = false;
	if(state_entry != NULL) {
		mb = state_entry->load_state(state_fio);
	}
	if(!mb) return false;
//	if(state_fio->FgetUint32_BE() != CHIP_STATE_VERSION) {
//		return false;
//	}
//	ratio_ = state_fio->FgetUint32_BE();
//	aml_ = state_fio->FgetUint32_BE();
//	pml_ = state_fio->FgetUint32_BE();
//	pmv_ = state_fio->FgetInt32_BE();
	return true;
}


// ---------------------------------------------------------------------------
//	Operator
//
bool FM::Operator::tablehasmade = false;
uint FM::Operator::sinetable[1024];
int32 FM::Operator::cltable[FM_CLENTS];

//	構築
FM::Operator::Operator()
: chip_(0)
{
	if (!tablehasmade)
		MakeTable();

	static int __num = 0;
	operators_num = __num++;
	// EG Part
	ar_ = dr_ = sr_ = rr_ = key_scale_rate_ = 0;
	ams_ = amtable[0][0];
	mute_ = false;
	keyon_ = false;
	tl_out_ = false;
	ssg_type_ = 0;

	// PG Part
	multiple_ = 0;
	detune_ = 0;
	detune2_ = 0;

	// LFO
	ms_ = 0;
	
//	Reset();
}

//	初期化
void FM::Operator::Reset()
{
	// EG part
	tl_ = tl_latch_ = 127;
	ShiftPhase(off);
	eg_count_ = 0;
	eg_curve_count_ = 0;
	ssg_phase_ = 0;

	// PG part
	pg_count_ = 0;

	// OP part
	out_ = out2_ = 0;

	param_changed_ = true;
	PARAMCHANGE(0);
}

void Operator::MakeTable()
{
	// 対数テーブルの作成
	assert(FM_CLENTS >= 256);

	int* p = cltable;
	int i;
	for (i=0; i<256; i++)
	{
		int v = int(floor(pow(2., 13. - i / 256.)));
		v = (v + 2) & ~3;
		*p++ = v;
		*p++ = -v;
	}
	while (p < cltable + FM_CLENTS)
	{
		*p = p[-512] / 2;
		p++;
	}

//	for (i=0; i<13*256; i++)
//		printf("%4d, %d, %d\n", i, cltable[i*2], cltable[i*2+1]);

	// サインテーブルの作成
	double log2 = log(2.);
	for (i=0; i<FM_OPSINENTS/2; i++)
	{
		double r = (i * 2 + 1) * FM_PI / FM_OPSINENTS;
		double q = -256 * log(sin(r)) / log2;
		uint s = (int) (floor(q + 0.5)) + 1;
//		printf("%d, %d\n", s, cltable[s * 2] / 8);
		sinetable[i]                  = s * 2 ;
		sinetable[FM_OPSINENTS / 2 + i] = s * 2 + 1;
	}

	::FM::MakeLFOTable();

	tablehasmade = true;
}



inline void FM::Operator::SetDPBN(uint dp, uint bn)
{
	dp_ = dp, bn_ = bn; param_changed_ = true; 
	PARAMCHANGE(1);
}


//	準備
void Operator::Prepare()
{
	if (param_changed_)
	{
		param_changed_ = false;
		//	PG Part
		pg_diff_ = (dp_ + dttable[detune_ + bn_]) * chip_->GetMulValue(detune2_, multiple_);
		pg_diff_ >>= (2 + FM_RATIOBITS - FM_PGBITS);
		pg_diff_lfo_ = pg_diff_ >> 11;

		// EG Part
		key_scale_rate_ = bn_ >> (3-ks_);
		tl_out_ = mute_ ? 0x3ff : tl_ * 8;
		
		switch (eg_phase_)
		{
		case attack:
			SetEGRate(ar_ ? Min(63, ar_ + key_scale_rate_) : 0);
			break;
		case decay:
			SetEGRate(dr_ ? Min(63, dr_ + key_scale_rate_) : 0);
			eg_level_on_next_phase_ = sl_ * 8;
			break;
		case sustain:
			SetEGRate(sr_ ? Min(63, sr_ + key_scale_rate_) : 0);
			break;
		case release:
			SetEGRate(Min(63, rr_ + key_scale_rate_));
			break;
		}

		// SSG-EG
		if (ssg_type_ && (eg_phase_ != release))
		{
			int m = ar_ >= (uint)((ssg_type_ == 8 || ssg_type_ == 12) ? 56 : 60);

			if (ssg_phase_ == -1)		// XXX quick fix
				ssg_phase_ = 0;
			assert(0 <= ssg_phase_ && ssg_phase_ <= 2);
			const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_];

			ssg_offset_ = table[0] * 0x200;
			ssg_vector_ = table[1];
		}
		// LFO
		ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0];
		EGUpdate();

		dbgopout_ = 0;
	}
}
//	envelop の eg_phase_ 変更
void Operator::ShiftPhase(EGPhase nextphase)
{
	switch (nextphase)
	{
	case attack:		// Attack Phase
		tl_ = tl_latch_;
		if (ssg_type_)
		{
			ssg_phase_ = ssg_phase_ + 1;
			if (ssg_phase_ > 2)
				ssg_phase_ = 1;
			
			int m = ar_ >= (uint)((ssg_type_ == 8 || ssg_type_ == 12) ? 56 : 60);

			assert(0 <= ssg_phase_ && ssg_phase_ <= 2);
			const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_];

			ssg_offset_ = table[0] * 0x200;
			ssg_vector_ = table[1];
		}
		if ((ar_ + key_scale_rate_) < 62)
		{
			SetEGRate(ar_ ? Min(63, ar_ + key_scale_rate_) : 0);
			eg_phase_ = attack;
			break;
		}
	case decay:			// Decay Phase
		if (sl_)
		{
			eg_level_ = 0;
			eg_level_on_next_phase_ = ssg_type_ ? Min(sl_ * 8, 0x200) : sl_ * 8;

			SetEGRate(dr_ ? Min(63, dr_ + key_scale_rate_) : 0);
			eg_phase_ = decay;
			break;
		}
	case sustain:		// Sustain Phase
		eg_level_ = sl_ * 8;
		eg_level_on_next_phase_ = ssg_type_ ? 0x200 : 0x400;

		SetEGRate(sr_ ? Min(63, sr_ + key_scale_rate_) : 0);
		eg_phase_ = sustain;
		break;
	
	case release:		// Release Phase
		if (ssg_type_)
		{
			eg_level_ = eg_level_ * ssg_vector_ + ssg_offset_;
			ssg_vector_ = 1;
			ssg_offset_ = 0;
		}
		if (eg_phase_ == attack || (eg_level_ < FM_EG_BOTTOM)) //0x400/* && eg_phase_ != off*/))
		{
			eg_level_on_next_phase_ = 0x400;
			SetEGRate(Min(63, rr_ + key_scale_rate_));
			eg_phase_ = release;
			break;
		}
	case off:			// off
	default:
		eg_level_ = FM_EG_BOTTOM;
		eg_level_on_next_phase_ = FM_EG_BOTTOM;
		EGUpdate();
		SetEGRate(0);
		eg_phase_ = off;
		break;
	}
}

//	Block/F-Num
void Operator::SetFNum(uint f)
{
	dp_ = (f & 2047) << ((f >> 11) & 7);
	bn_ = notetable[(f >> 7) & 127];
	param_changed_ = true;
	PARAMCHANGE(2);
}

//	１サンプル合成

//	ISample を envelop count (2π) に変換するシフト量
#define IS2EC_SHIFT		((20 + FM_PGBITS) - 13)


// 入力: s = 20+FM_PGBITS = 29
#define Sine(s)	sinetable[((s) >> (20+FM_PGBITS-FM_OPSINBITS))&(FM_OPSINENTS-1)]
#define SINE(s) sinetable[(s) & (FM_OPSINENTS-1)]

inline FM::ISample Operator::LogToLin(uint a)
{
#if 1 // FM_CLENTS < 0xc00		// 400 for TL, 400 for ENV, 400 for LFO.
	return (a < FM_CLENTS) ? cltable[a] : 0;
#else
	return cltable[a];
#endif
}

inline void Operator::EGUpdate()
{
	if (!ssg_type_)
	{
		eg_out_ = Min(tl_out_ + eg_level_, 0x3ff) << (1 + 2);
	}
	else
	{
		eg_out_ = Min(tl_out_ + eg_level_ * ssg_vector_ + ssg_offset_, 0x3ff) << (1 + 2);
	}
}

inline void Operator::SetEGRate(uint rate)
{
	eg_rate_ = rate;
	eg_count_diff_ = decaytable2[rate / 4] * chip_->GetRatio();
}

//	EG 計算
void FM::Operator::EGCalc()
{
	eg_count_ = (2047 * 3) << FM_RATIOBITS;				// ##この手抜きは再現性を低下させる
	
	if (eg_phase_ == attack)
	{
		int c = attacktable[eg_rate_][eg_curve_count_ & 7];
		if (c >= 0)
		{
			eg_level_ -= 1 + (eg_level_ >> c);
			if (eg_level_ <= 0)
				ShiftPhase(decay);
		}
		EGUpdate();
	}
	else
	{
		if (!ssg_type_)
		{
			eg_level_ += decaytable1[eg_rate_][eg_curve_count_ & 7];
			if (eg_level_ >= eg_level_on_next_phase_)
				ShiftPhase(EGPhase(eg_phase_+1));
			EGUpdate();
		}
		else
		{
			eg_level_ += 4 * decaytable1[eg_rate_][eg_curve_count_ & 7];
			if (eg_level_ >= eg_level_on_next_phase_)
			{
				switch (eg_phase_)
				{
				case decay:
					ShiftPhase(sustain);
					break;
				case sustain:
					ShiftPhase(attack);
					break;
				case release:
					ShiftPhase(off);
					break;
				}
			}
			EGUpdate();
		}
	}
	eg_curve_count_++;
}

inline void FM::Operator::EGStep()
{
	eg_count_ -= eg_count_diff_;

	// EG の変化は全スロットで同期しているという噂もある
	if (eg_count_ <= 0)
		EGCalc();
}

//	PG 計算
//	ret:2^(20+PGBITS) / cycle
inline uint32 FM::Operator::PGCalc()
{
	uint32 ret = pg_count_;
	pg_count_ += pg_diff_;
	dbgpgout_ = ret;
	return ret;
}

inline uint32 FM::Operator::PGCalcL()
{
	uint32 ret = pg_count_;
	pg_count_ += pg_diff_ + ((pg_diff_lfo_ * chip_->GetPMV()) >> 5);// & -(1 << (2+IS2EC_SHIFT)));
	dbgpgout_ = ret;
	return ret /* + pmv * pg_diff_;*/;
}

//	OP 計算
//	in: ISample (最大 8π)
inline FM::ISample FM::Operator::Calc(ISample in)
{
	EGStep();
	out2_ = out_;

	int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS);
	pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT));
	out_ = LogToLin(eg_out_ + SINE(pgin));

	dbgopout_ = out_;
	return out_;
}

inline FM::ISample FM::Operator::CalcL(ISample in)
{
	EGStep();

	int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS);
	pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT));
	out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]);

	dbgopout_ = out_;
	return out_;
}

inline FM::ISample FM::Operator::CalcN(uint noise)
{
	EGStep();
	
	int lv = Max(0, 0x3ff - (tl_out_ + eg_level_)) << 1;
	
	// noise & 1 ? lv : -lv と等価 
	noise = (noise & 1) - 1;
	out_ = (lv + noise) ^ noise;

	dbgopout_ = out_;
	return out_;
}

//	OP (FB) 計算
//	Self Feedback の変調最大 = 4π
inline FM::ISample FM::Operator::CalcFB(uint fb)
{
	EGStep();

	ISample in = out_ + out2_;
	out2_ = out_;

	int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS);
	if (fb < 31)
	{
		pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS);
	}
	out_ = LogToLin(eg_out_ + SINE(pgin));
	dbgopout_ = out2_;

	return out2_;
}

inline FM::ISample FM::Operator::CalcFBL(uint fb)
{
	EGStep();
	
	ISample in = out_ + out2_;
	out2_ = out_;

	int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS);
	if (fb < 31)
	{
		pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS);
	}

	out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]);
	dbgopout_ = out_;

	return out_;
}

// ---------------------------------------------------------------------------
//	ステートセーブ
//
#define OPERATOR_STATE_VERSION	2

void Operator::DeclState(void *f)
{
	p_logger = (CSP_Logger *)f;
	state_entry = new csp_state_utils(OPERATOR_STATE_VERSION, operators_num, _T("FMGEN::Operator::"), p_logger);

	DECL_STATE_ENTRY_INT32(out_);
	DECL_STATE_ENTRY_INT32(out2_);
	DECL_STATE_ENTRY_INT32(in2_);
	DECL_STATE_ENTRY_UINT32(dp_);
	DECL_STATE_ENTRY_UINT32(detune_);
	DECL_STATE_ENTRY_UINT32(detune2_);
	DECL_STATE_ENTRY_UINT32(multiple_);
	DECL_STATE_ENTRY_UINT32(pg_count_);
	DECL_STATE_ENTRY_UINT32(pg_diff_);
	DECL_STATE_ENTRY_INT32(pg_diff_lfo_);
	DECL_STATE_ENTRY_UINT32(bn_);
	DECL_STATE_ENTRY_INT32(eg_level_);
	DECL_STATE_ENTRY_INT32(eg_level_on_next_phase_);
	DECL_STATE_ENTRY_INT32(eg_count_);
	DECL_STATE_ENTRY_INT32(eg_count_diff_);
	DECL_STATE_ENTRY_INT32(eg_out_);
	DECL_STATE_ENTRY_INT32(tl_out_);
	DECL_STATE_ENTRY_INT32(eg_rate_);
	DECL_STATE_ENTRY_INT32(eg_curve_count_);
	DECL_STATE_ENTRY_INT32(ssg_offset_);
	DECL_STATE_ENTRY_INT32(ssg_vector_);
	DECL_STATE_ENTRY_INT32(ssg_phase_);
	DECL_STATE_ENTRY_UINT32(key_scale_rate_);
	DECL_STATE_ENTRY_INT32(tmp_eg_phase);
	DECL_STATE_ENTRY_INT32(tmp_ams);
	DECL_STATE_ENTRY_UINT32(ms_);
	DECL_STATE_ENTRY_UINT32(tl_);
	DECL_STATE_ENTRY_UINT32(tl_latch_);
	DECL_STATE_ENTRY_UINT32(ar_);
	DECL_STATE_ENTRY_UINT32(dr_);
	DECL_STATE_ENTRY_UINT32(sr_);
	DECL_STATE_ENTRY_UINT32(sl_);
	DECL_STATE_ENTRY_UINT32(rr_);
	DECL_STATE_ENTRY_UINT32(ks_);
	DECL_STATE_ENTRY_UINT32(ssg_type_);
	DECL_STATE_ENTRY_BOOL(keyon_);
	DECL_STATE_ENTRY_BOOL(amon_);
	DECL_STATE_ENTRY_BOOL(param_changed_);
	DECL_STATE_ENTRY_BOOL(mute_);
	DECL_STATE_ENTRY_INT32(dbgopout_);
	DECL_STATE_ENTRY_INT32(dbgpgout_);
}

void Operator::SaveState(void *f)
{
	FILEIO *state_fio = (FILEIO *)f;

	tmp_eg_phase = static_cast<int>(eg_phase_);
	tmp_ams = (int)(ams_ - &amtable[0][0][0]);
	if(state_entry != NULL) {
		state_entry->save_state(state_fio);
	}
//	state_fio->FputUint32_BE(OPERATOR_STATE_VERSION);
	
//	state_fio->FputInt32_BE(out_);
//	state_fio->FputInt32_BE(out2_);
//	state_fio->FputInt32_BE(in2_);
//	state_fio->FputUint32_BE(dp_);
//	state_fio->FputUint32_BE(detune_);
//	state_fio->FputUint32_BE(detune2_);
//	state_fio->FputUint32_BE(multiple_);
//	state_fio->FputUint32_BE(pg_count_);
//	state_fio->FputUint32_BE(pg_diff_);
//	state_fio->FputInt32_BE(pg_diff_lfo_);
//	state_fio->FputUint32_BE(bn_);
//	state_fio->FputInt32_BE(eg_level_);
//	state_fio->FputInt32_BE(eg_level_on_next_phase_);
//	state_fio->FputInt32_BE(eg_count_);
//	state_fio->FputInt32_BE(eg_count_diff_);
//	state_fio->FputInt32_BE(eg_out_);
//	state_fio->FputInt32_BE(tl_out_);
//	state_fio->FputInt32_BE(eg_rate_);
//	state_fio->FputInt32_BE(eg_curve_count_);
//	state_fio->FputInt32_BE(ssg_offset_);
//	state_fio->FputInt32_BE(ssg_vector_);
//	state_fio->FputInt32_BE(ssg_phase_);
//	state_fio->FputUint32_BE(key_scale_rate_);
//	state_fio->FputInt32_BE(static_cast<int>(eg_phase_));
//	state_fio->FputInt32_BE((int)(ams_ - &amtable[0][0][0]));
//	state_fio->FputUint32_BE(ms_);
//	state_fio->FputUint32_BE(tl_);
//	state_fio->FputUint32_BE(tl_latch_);
//	state_fio->FputUint32_BE(ar_);
//	state_fio->FputUint32_BE(dr_);
//	state_fio->FputUint32_BE(sr_);
//	state_fio->FputUint32_BE(sl_);
//	state_fio->FputUint32_BE(rr_);
//	state_fio->FputUint32_BE(ks_);
//	state_fio->FputUint32_BE(ssg_type_);
//	state_fio->FputBool(keyon_);
//	state_fio->FputBool(amon_);
//	state_fio->FputBool(param_changed_);
//	state_fio->FputBool(mute_);
//	state_fio->FputInt32_BE(dbgopout_);
//	state_fio->FputInt32_BE(dbgpgout_);
}

bool Operator::LoadState(void *f)
{
	FILEIO *state_fio = (FILEIO *)f;
	bool mb = false;
	if(state_entry != NULL) {
		mb = state_entry->load_state(state_fio);
	}
	if(!mb) return false;
	eg_phase_ = static_cast<EGPhase>(tmp_eg_phase);
	ams_ = &amtable[0][0][0] + tmp_ams;
	
//	if(state_fio->FgetUint32_BE() != OPERATOR_STATE_VERSION) {
//		return false;
//	}
//	out_ = state_fio->FgetInt32_BE();
//	out2_ = state_fio->FgetInt32_BE();
//	in2_ = state_fio->FgetInt32_BE();
//	dp_ = state_fio->FgetUint32_BE();
//	detune_ = state_fio->FgetUint32_BE();
//	detune2_ = state_fio->FgetUint32_BE();
//	multiple_ = state_fio->FgetUint32_BE();
//	pg_count_ = state_fio->FgetUint32_BE();
//	pg_diff_ = state_fio->FgetUint32_BE();
//	pg_diff_lfo_ = state_fio->FgetInt32_BE();
//	bn_ = state_fio->FgetUint32_BE();
//	eg_level_ = state_fio->FgetInt32_BE();
//	eg_level_on_next_phase_ = state_fio->FgetInt32_BE();
//	eg_count_ = state_fio->FgetInt32_BE();
//	eg_count_diff_ = state_fio->FgetInt32_BE();
//	eg_out_ = state_fio->FgetInt32_BE();
//	tl_out_ = state_fio->FgetInt32_BE();
//	eg_rate_ = state_fio->FgetInt32_BE();
//	eg_curve_count_ = state_fio->FgetInt32_BE();
//	ssg_offset_ = state_fio->FgetInt32_BE();
//	ssg_vector_ = state_fio->FgetInt32_BE();
//	ssg_phase_ = state_fio->FgetInt32_BE();
//	key_scale_rate_ = state_fio->FgetUint32_BE();
//	eg_phase_ = static_cast<EGPhase>(state_fio->FgetInt32_BE());
//	ams_ = &amtable[0][0][0] + state_fio->FgetInt32_BE();
//	ms_ = state_fio->FgetUint32_BE();
//	tl_ = state_fio->FgetUint32_BE();
//	tl_latch_ = state_fio->FgetUint32_BE();
//	ar_ = state_fio->FgetUint32_BE();
//	dr_ = state_fio->FgetUint32_BE();
//	sr_ = state_fio->FgetUint32_BE();
//	sl_ = state_fio->FgetUint32_BE();
//	rr_ = state_fio->FgetUint32_BE();
//	ks_ = state_fio->FgetUint32_BE();
//	ssg_type_ = state_fio->FgetUint32_BE();
//	keyon_ = state_fio->FgetBool();
//	amon_ = state_fio->FgetBool();
//	param_changed_ = state_fio->FgetBool();
//	mute_ = state_fio->FgetBool();
//	dbgopout_ = state_fio->FgetInt32_BE();
//	dbgpgout_ = state_fio->FgetInt32_BE();
	return true;
}

#undef Sine

// ---------------------------------------------------------------------------
//	4-op Channel
//
const uint8 Channel4::fbtable[8] = { 31, 7, 6, 5, 4, 3, 2, 1 };
int Channel4::kftable[64];

bool Channel4::tablehasmade = false;


Channel4::Channel4()
{
	if (!tablehasmade)
		MakeTable();

	static int __num = 0;
	channel4s_num = __num++;
	
	SetAlgorithm(0);
	pms = pmtable[0][0];
}

void Channel4::MakeTable()
{
	// 100/64 cent =  2^(i*100/64*1200)
	for (int i=0; i<64; i++)
	{
		kftable[i] = int(0x10000 * pow(2., i / 768.) );
	}
}

// リセット
void Channel4::Reset()
{
	op[0].Reset();
	op[1].Reset();
	op[2].Reset();
	op[3].Reset();
}

//	Calc の用意
int Channel4::Prepare()
{
	op[0].Prepare();
	op[1].Prepare();
	op[2].Prepare();
	op[3].Prepare();
	
	pms = pmtable[op[0].type_][op[0].ms_ & 7];
	int key = (op[0].IsOn() | op[1].IsOn() | op[2].IsOn() | op[3].IsOn()) ? 1 : 0;
	int lfo = op[0].ms_ & (op[0].amon_ | op[1].amon_ | op[2].amon_ | op[3].amon_ ? 0x37 : 7) ? 2 : 0;
	return key | lfo;
}

//	F-Number/BLOCK を設定
void Channel4::SetFNum(uint f)
{
	for (int i=0; i<4; i++)
		op[i].SetFNum(f);
}

//	KC/KF を設定
void Channel4::SetKCKF(uint kc, uint kf)
{
	const static uint kctable[16] = 
	{ 
		5197, 5506, 5833, 6180, 6180, 6547, 6937, 7349, 
		7349, 7786, 8249, 8740, 8740, 9259, 9810, 10394, 
	};

	int oct = 19 - ((kc >> 4) & 7);

//printf("%p", this);
	uint kcv = kctable[kc & 0x0f];
	kcv = (kcv + 2) / 4 * 4;
//printf(" %.4x", kcv);
	uint dp = kcv * kftable[kf & 0x3f];
//printf(" %.4x %.4x %.8x", kcv, kftable[kf & 0x3f], dp >> oct);
	dp >>= 16 + 3;
	dp <<= 16 + 3;
	dp >>= oct;	
	uint bn = (kc >> 2) & 31;
	op[0].SetDPBN(dp, bn);
	op[1].SetDPBN(dp, bn);
	op[2].SetDPBN(dp, bn);
	op[3].SetDPBN(dp, bn);
//printf(" %.8x\n", dp);
}

//	キー制御
void Channel4::KeyControl(uint key)
{
	if (key & 0x1) op[0].KeyOn(); else op[0].KeyOff();
	if (key & 0x2) op[1].KeyOn(); else op[1].KeyOff();
	if (key & 0x4) op[2].KeyOn(); else op[2].KeyOff();
	if (key & 0x8) op[3].KeyOn(); else op[3].KeyOff();
}

//	アルゴリズムを設定
void Channel4::SetAlgorithm(uint algo)
{
	static const uint8 table1[8][6] = 
	{
		{ 0, 1, 1, 2, 2, 3 },	{ 1, 0, 0, 1, 1, 2 },
		{ 1, 1, 1, 0, 0, 2 },	{ 0, 1, 2, 1, 1, 2 },
		{ 0, 1, 2, 2, 2, 1 },	{ 0, 1, 0, 1, 0, 1 },
		{ 0, 1, 2, 1, 2, 1 },	{ 1, 0, 1, 0, 1, 0 },
	};

	in [0] = &buf[table1[algo][0]];
	out[0] = &buf[table1[algo][1]];
	in [1] = &buf[table1[algo][2]];
	out[1] = &buf[table1[algo][3]];
	in [2] = &buf[table1[algo][4]];
	out[2] = &buf[table1[algo][5]];

	op[0].ResetFB();
	algo_ = algo;
}

//  合成
ISample Channel4::Calc()
{
	int r = 0;
	switch (algo_)
	{
	case 0:
		op[2].Calc(op[1].Out());
		op[1].Calc(op[0].Out());
		r = op[3].Calc(op[2].Out());
		op[0].CalcFB(fb);
		break;
	case 1:
		op[2].Calc(op[0].Out() + op[1].Out());
		op[1].Calc(0);
		r = op[3].Calc(op[2].Out());
		op[0].CalcFB(fb);
		break;
	case 2:
		op[2].Calc(op[1].Out());
		op[1].Calc(0);
		r = op[3].Calc(op[0].Out() + op[2].Out());
		op[0].CalcFB(fb);
		break;
	case 3:
		op[2].Calc(0);
		op[1].Calc(op[0].Out());
		r = op[3].Calc(op[1].Out() + op[2].Out());
		op[0].CalcFB(fb);
		break;
	case 4:
		op[2].Calc(0);
		r = op[1].Calc(op[0].Out());
		r += op[3].Calc(op[2].Out());
		op[0].CalcFB(fb);
		break;
	case 5:
		r =  op[2].Calc(op[0].Out());
		r += op[1].Calc(op[0].Out());
		r += op[3].Calc(op[0].Out());
		op[0].CalcFB(fb);
		break;
	case 6:
		r  = op[2].Calc(0);
		r += op[1].Calc(op[0].Out());
		r += op[3].Calc(0);
		op[0].CalcFB(fb);
		break;
	case 7:
		r  = op[2].Calc(0);
		r += op[1].Calc(0);
		r += op[3].Calc(0);
		r += op[0].CalcFB(fb);
		break;
	}
	return r;
}

//  合成
ISample Channel4::CalcL()
{
	chip_->SetPMV(pms[chip_->GetPML()]);

	int r = 0;
	switch (algo_)
	{
	case 0:
		op[2].CalcL(op[1].Out());
		op[1].CalcL(op[0].Out());
		r = op[3].CalcL(op[2].Out());
		op[0].CalcFBL(fb);
		break;
	case 1:
		op[2].CalcL(op[0].Out() + op[1].Out());
		op[1].CalcL(0);
		r = op[3].CalcL(op[2].Out());
		op[0].CalcFBL(fb);
		break;
	case 2:
		op[2].CalcL(op[1].Out());
		op[1].CalcL(0);
		r = op[3].CalcL(op[0].Out() + op[2].Out());
		op[0].CalcFBL(fb);
		break;
	case 3:
		op[2].CalcL(0);
		op[1].CalcL(op[0].Out());
		r = op[3].CalcL(op[1].Out() + op[2].Out());
		op[0].CalcFBL(fb);
		break;
	case 4:
		op[2].CalcL(0);
		r = op[1].CalcL(op[0].Out());
		r += op[3].CalcL(op[2].Out());
		op[0].CalcFBL(fb);
		break;
	case 5:
		r =  op[2].CalcL(op[0].Out());
		r += op[1].CalcL(op[0].Out());
		r += op[3].CalcL(op[0].Out());
		op[0].CalcFBL(fb);
		break;
	case 6:
		r  = op[2].CalcL(0);
		r += op[1].CalcL(op[0].Out());
		r += op[3].CalcL(0);
		op[0].CalcFBL(fb);
		break;
	case 7:
		r  = op[2].CalcL(0);
		r += op[1].CalcL(0);
		r += op[3].CalcL(0);
		r += op[0].CalcFBL(fb);
		break;
	}
	return r;
}

//  合成
ISample Channel4::CalcN(uint noise)
{
	buf[1] = buf[2] = buf[3] = 0;

	buf[0] = op[0].out_; op[0].CalcFB(fb);
	*out[0] += op[1].Calc(*in[0]);
	*out[1] += op[2].Calc(*in[1]);
	int o = op[3].out_;
	op[3].CalcN(noise);
	return *out[2] + o;
}

//  合成
ISample Channel4::CalcLN(uint noise)
{
	chip_->SetPMV(pms[chip_->GetPML()]);
	buf[1] = buf[2] = buf[3] = 0;

	buf[0] = op[0].out_; op[0].CalcFBL(fb); 
	*out[0] += op[1].CalcL(*in[0]);
	*out[1] += op[2].CalcL(*in[1]);
	int o = op[3].out_;
	op[3].CalcN(noise);
	return *out[2] + o;
}

// ---------------------------------------------------------------------------
//	ステートセーブ
//
#define CHANNEL4_STATE_VERSION	2
void Channel4::DeclState(void *f)
{
	p_logger = (CSP_Logger *)f;
	state_entry = new csp_state_utils(CHIP_STATE_VERSION, channel4s_num, _T("FMGEN::Channel4::"), p_logger);

	DECL_STATE_ENTRY_UINT32(fb);
	DECL_STATE_ENTRY_1D_ARRAY(buf, sizeof(buf) / sizeof(int));
	for(int i = 0; i < 3; i++) {
		DECL_STATE_ENTRY_INT32_MEMBER((tmp_in_bufptr[i]), i);
		DECL_STATE_ENTRY_INT32_MEMBER((tmp_out_bufptr[i]), i);
	}
	DECL_STATE_ENTRY_INT32(tmp_pms);
	DECL_STATE_ENTRY_INT32(algo_);
	for(int i = 0; i < 4; i++) {
		op[i].DeclState(f);
	}
	
}
void Channel4::SaveState(void *f)
{
	FILEIO *state_fio = (FILEIO *)f;
	
	for(int i = 0; i < 3; i++) {
		tmp_in_bufptr[i] = (int)(in[i] - &buf[0]);
		tmp_out_bufptr[i] = (int)(out[i] - &buf[0]);
	}
	tmp_pms = (int)(pms - &pmtable[0][0][0]);
	if(state_entry != NULL) {
		state_entry->save_state(state_fio);
	}
//	state_fio->FputUint32_BE(CHANNEL4_STATE_VERSION);
	
//	state_fio->FputUint32_BE(fb);
//	state_fio->Fwrite(buf, sizeof(buf), 1);
//	for(int i = 0; i < 3; i++) {
//		tmp_in_buffer[i] = (int)(in[i] - &buf[0])
//		tmp_out_buffer[i] = (int)(out[i] - &buf[0])
//		state_fio->FputInt32_BE((int)(in [i] - &buf[0]));
//		state_fio->FputInt32_BE((int)(out[i] - &buf[0]));
//	}
//	tmp_pms = (int)(pms - &pmtable[0][0][0]);
//	state_fio->FputInt32_BE((int)(pms - &pmtable[0][0][0]));
//	state_fio->FputInt32_BE(algo_);
	for(int i = 0; i < 4; i++) {
		op[i].SaveState(f);
	}
}

bool Channel4::LoadState(void *f)
{
	FILEIO *state_fio = (FILEIO *)f;
	
	bool mb = false;
	if(state_entry != NULL) {
		mb = state_entry->load_state(state_fio);
	}
	if(!mb) return false;
//	if(state_fio->FgetUint32_BE() != CHANNEL4_STATE_VERSION) {
//		return false;
//	}
	//fb = state_fio->FgetUint32_BE();
	//state_fio->Fread(buf, sizeof(buf), 1);
	//for(int i = 0; i < 3; i++) {
	//	in [i] = &buf[0] + state_fio->FgetInt32_BE();
	//	out[i] = &buf[0] + state_fio->FgetInt32_BE();
	//}
	//pms = &pmtable[0][0][0] + state_fio->FgetInt32_BE();
	//algo_ = state_fio->FgetInt32_BE();
	
	for(int i = 0; i < 3; i++) {
		in[i] = &buf[0] + tmp_in_bufptr[i];
		out[i] = &buf[0] + tmp_in_bufptr[i];
	}
	pms =&pmtable[0][0][0] + tmp_pms;
	for(int i = 0; i < 4; i++) {
		if(!op[i].LoadState(f)) {
			return false;
		}
	}
	return true;
}

}	// namespace FM
