shithub: riscv

ref: 2810cc5d0c01b06e678b50995edb724c739b8eed
dir: /sys/src/games/opl3/opl3.c/

View raw version
#include <u.h>
#include <libc.h>

typedef struct Op Op;
typedef struct Chan Chan;

#define Clk	14318180.0
#define FREQ_SH	16
#define EG_SH	16
#define LFO_SH	24
#define TIMER_SH	16
#define FREQ_MASK	((1 << FREQ_SH) - 1)
#define ENV_BITS	10
#define ENV_LEN	(1 << ENV_BITS)
#define ENV_STEP	(128.0 / ENV_LEN)
#define MAX_ATT_INDEX	((1 << ENV_BITS - 1) - 1)
#define MIN_ATT_INDEX	0
#define SIN_BITS	10
#define SIN_LEN	(1 << SIN_BITS)
#define SIN_MASK	(SIN_LEN - 1)
#define TL_RES_LEN	256

enum{
	EG_OFF,
	EG_REL,
	EG_SUS,
	EG_DEC,
	EG_ATT,
};

struct Op
{
	u32int ar;	/* attack rate: AR<<2 */
	u32int dr;	/* decay rate: DR<<2 */
	u32int rr;	/* release rate:RR<<2 */
	u8int KSR;	/* key scale rate */
	u8int ksl;	/* keyscale level */
	u8int ksr;	/* key scale rate: kcode>>KSR */
	u8int mul;	/* multiple: mul_tab[ML] */

	/* Phase Generator */
	u32int Cnt;	/* frequency counter */
	u32int Incr;	/* frequency counter step */
	u8int FB;	/* feedback shift value */
	s32int *connect;	/* slot output pointer */
	s32int op1_out[2];	/* slot1 output for feedback */
	u8int CON;	/* connection (algorithm) type */

	/* Envelope Generator */
	u8int eg_type;	/* percussive/non-percussive mode */
	u8int state;	/* phase type */
	u32int TL;	/* total level: TL << 2 */
	s32int TLL;	/* adjusted now TL */
	s32int volume;	/* envelope counter */
	u32int sl;	/* sustain level: sl_tab[SL] */

	u32int eg_m_ar;	/* (attack state) */
	u8int eg_sh_ar;	/* (attack state) */
	u8int eg_sel_ar;	/* (attack state) */
	u32int eg_m_dr;	/* (decay state) */
	u8int eg_sh_dr;	/* (decay state) */
	u8int eg_sel_dr;	/* (decay state) */
	u32int eg_m_rr;	/* (release state) */
	u8int eg_sh_rr;	/* (release state) */
	u8int eg_sel_rr;	/* (release state) */

	u32int key;

	u32int AMmask;	/* LFO Amplitude Modulation enable mask */
	u8int vib;	/* LFO Phase Modulation enable flag (active high)*/

	u8int waveform_number;
	uint wavetable;
};

struct Chan
{
	Op SLOT[2];
	u32int block_fnum;	/* block+fnum */
	u32int fc;	/* Freq. Increment base */
	u32int ksl_base;	/* KeyScaleLevel Base step */
	u8int kcode;	/* key code (for key scaling) */
	u8int extended;
};
static Chan chs[18];
static u32int pan[18*4];	/* channels output masks (0xffffffff = enable); 4 masks per one channel */
static u32int pan_ctrl_value[18];	/* output control values 1 per one channel (1 value contains 4 masks) */
static int chanout[18];
static int phase_modulation, phase_modulation2;	/* phase modulation input (SLOT 2 and 3/4) */
static u32int eg_cnt;	/* global envelope generator counter */
static u32int eg_timer;	/* global envelope generator counter works at frequency = chipclock/288 (288=8*36) */
static u32int eg_timer_add;	/* step of eg_timer */
static u32int eg_timer_overflow;	/* envelope generator timer overlfows every 1 sample (on real chip) */
static u32int fn_tab[1024];
static u32int LFO_AM;
static s32int LFO_PM;
static u8int lfo_am_depth, lfo_pm_depth_range;
static u32int lfo_am_cnt, lfo_am_inc, lfo_pm_cnt, lfo_pm_inc;
static u32int noise_rng, noise_p, noise_f;
static int OPL3_mode, nts;
static u8int rhythm;

static int slot_array[32]=
{
	0, 2, 4, 1, 3, 5,-1,-1,
	6, 8,10, 7, 9,11,-1,-1,
	12,14,16,13,15,17,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1
};

/* key scale level */
/* table is 3dB/octave , DV converts this into 6dB/octave */
/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
#define DV (0.1875/2.0)
static u32int ksl_tab[8*16]=
{
	/* OCT 0 */
		0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
		0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
		0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
		0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
	/* OCT 1 */
		0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
		0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
		0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
		1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
	/* OCT 2 */
		0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
		0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
		3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
		4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
	/* OCT 3 */
		0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
		3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
		6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
		7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
	/* OCT 4 */
		0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
		6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
		9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
		10.875/DV,11.250/DV,11.625/DV,12.000/DV,
	/* OCT 5 */
		0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
		9.000/DV,10.125/DV,10.875/DV,11.625/DV,
		12.000/DV,12.750/DV,13.125/DV,13.500/DV,
		13.875/DV,14.250/DV,14.625/DV,15.000/DV,
	/* OCT 6 */
		0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
		12.000/DV,13.125/DV,13.875/DV,14.625/DV,
		15.000/DV,15.750/DV,16.125/DV,16.500/DV,
		16.875/DV,17.250/DV,17.625/DV,18.000/DV,
	/* OCT 7 */
		0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
		15.000/DV,16.125/DV,16.875/DV,17.625/DV,
		18.000/DV,18.750/DV,19.125/DV,19.500/DV,
		19.875/DV,20.250/DV,20.625/DV,21.000/DV
};
#undef DV

/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */
static u32int ksl_shift[4] = { 31, 1, 2, 0 };
/* sustain level table (3dB per step) */
/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
#define SC(db) (u32int) (db * (2.0/ENV_STEP))
static u32int sl_tab[16]={
	SC(0),SC(1),SC(2),SC(3),SC(4),SC(5),SC(6),SC(7),
	SC(8),SC(9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
};
#undef SC
#define RATE_STEPS (8)
static uchar eg_inc[15*RATE_STEPS]={
/*cycle:0 1 2 3 4 5 6 7*/

/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */

/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */

/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */

/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 for decay */
/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */
/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
};
#define O(a) (a*RATE_STEPS)

/* note that there is no O(13) in this table - it's directly in the code */
static uchar eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
/* 16 infinite time rates */
O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),

/* rates 00-12 */
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),
O(0),O(1),O(2),O(3),

/* rate 13 */
O(4),O(5),O(6),O(7),

/* rate 14 */
O(8),O(9),O(10),O(11),

/* rate 15 */
O(12),O(12),O(12),O(12),

/* 16 dummy rates (same as 15 3) */
O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),

};
#undef O

/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */

#define O(a) (a*1)
static uchar eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
/* 16 infinite time rates */
O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),

/* rates 00-12 */
O(12),O(12),O(12),O(12),
O(11),O(11),O(11),O(11),
O(10),O(10),O(10),O(10),
O(9),O(9),O(9),O(9),
O(8),O(8),O(8),O(8),
O(7),O(7),O(7),O(7),
O(6),O(6),O(6),O(6),
O(5),O(5),O(5),O(5),
O(4),O(4),O(4),O(4),
O(3),O(3),O(3),O(3),
O(2),O(2),O(2),O(2),
O(1),O(1),O(1),O(1),
O(0),O(0),O(0),O(0),

/* rate 13 */
O(0),O(0),O(0),O(0),

/* rate 14 */
O(0),O(0),O(0),O(0),

/* rate 15 */
O(0),O(0),O(0),O(0),

/* 16 dummy rates (same as 15 3) */
O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),

};
#undef O
/* multiple table */
#define ML 2
static u8int mul_tab[16]= {
/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
	ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML,
	8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML
};
#undef ML

#define TL_TAB_LEN (13*2*TL_RES_LEN)
static int tl_tab[TL_TAB_LEN];

#define ENV_QUIET (TL_TAB_LEN>>4)

static uint sin_tab[SIN_LEN * 8];

#define LFO_AM_TAB_ELEMENTS 210

static u8int lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
0,0,0,0,0,0,0,
1,1,1,1,
2,2,2,2,
3,3,3,3,
4,4,4,4,
5,5,5,5,
6,6,6,6,
7,7,7,7,
8,8,8,8,
9,9,9,9,
10,10,10,10,
11,11,11,11,
12,12,12,12,
13,13,13,13,
14,14,14,14,
15,15,15,15,
16,16,16,16,
17,17,17,17,
18,18,18,18,
19,19,19,19,
20,20,20,20,
21,21,21,21,
22,22,22,22,
23,23,23,23,
24,24,24,24,
25,25,25,25,
26,26,26,
25,25,25,25,
24,24,24,24,
23,23,23,23,
22,22,22,22,
21,21,21,21,
20,20,20,20,
19,19,19,19,
18,18,18,18,
17,17,17,17,
16,16,16,16,
15,15,15,15,
14,14,14,14,
13,13,13,13,
12,12,12,12,
11,11,11,11,
10,10,10,10,
9,9,9,9,
8,8,8,8,
7,7,7,7,
6,6,6,6,
5,5,5,5,
4,4,4,4,
3,3,3,3,
2,2,2,2,
1,1,1,1
};

/* LFO Phase Modulation table (verified on real YM3812) */
static s8int lfo_pm_table[8*8*2] = {
/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/

/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/

/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/

/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/

/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/

/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/

/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/

/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
};

#define SLOT7_1 (&chs[7].SLOT[0])
#define SLOT7_2 (&chs[7].SLOT[1])
#define SLOT8_1 (&chs[8].SLOT[0])
#define SLOT8_2 (&chs[8].SLOT[1])

static void
advance_lfo(void)
{
	u8int tmp;

	lfo_am_cnt += lfo_am_inc;
	if(lfo_am_cnt >= ((u32int)LFO_AM_TAB_ELEMENTS<<LFO_SH))
		lfo_am_cnt -= ((u32int)LFO_AM_TAB_ELEMENTS<<LFO_SH);
	tmp = lfo_am_table[lfo_am_cnt >> LFO_SH];
	if(lfo_am_depth)
		LFO_AM = tmp;
	else
		LFO_AM = tmp>>2;
	lfo_pm_cnt += lfo_pm_inc;
	LFO_PM = (lfo_pm_cnt>>LFO_SH & 7) | lfo_pm_depth_range;
}

static void
advance(void)
{
	Chan *CH;
	Op *op;
	int i;

	eg_timer += eg_timer_add;
	while (eg_timer >= eg_timer_overflow){
		eg_timer -= eg_timer_overflow;
		eg_cnt++;
		for (i=0; i<9*2*2; i++){
			CH = &chs[i/2];
			op = &CH->SLOT[i&1];
			switch(op->state){
			case EG_ATT:
				if(!(eg_cnt & op->eg_m_ar)){
					op->volume += (s32int)(~op->volume *
												(eg_inc[op->eg_sel_ar + ((eg_cnt>>op->eg_sh_ar)&7)])
												) >>3;
					if(op->volume <= MIN_ATT_INDEX){
						op->volume = MIN_ATT_INDEX;
						op->state = EG_DEC;
					}
				}
				break;
			case EG_DEC:
				if(!(eg_cnt & op->eg_m_dr)){
					op->volume += eg_inc[op->eg_sel_dr + ((eg_cnt>>op->eg_sh_dr)&7)];
					if(op->volume >= op->sl)
						op->state = EG_SUS;
				}
				break;
			case EG_SUS:
				if(op->eg_type)
				{
				}else{
					if(!(eg_cnt & op->eg_m_rr)){
						op->volume += eg_inc[op->eg_sel_rr + ((eg_cnt>>op->eg_sh_rr)&7)];
						if(op->volume >= MAX_ATT_INDEX)
							op->volume = MAX_ATT_INDEX;
					}
				}
				break;
			case EG_REL:
				if(!(eg_cnt & op->eg_m_rr)){
					op->volume += eg_inc[op->eg_sel_rr + ((eg_cnt>>op->eg_sh_rr)&7)];
					if(op->volume >= MAX_ATT_INDEX){
						op->volume = MAX_ATT_INDEX;
						op->state = EG_OFF;
					}
				}
				break;
			}
		}
	}
	for (i=0; i<9*2*2; i++){
		CH = &chs[i/2];
		op = &CH->SLOT[i&1];
		if(op->vib){
			u8int block;
			uint block_fnum = CH->block_fnum;
			uint fnum_lfo = (block_fnum&0x0380) >> 7;
			int lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16*fnum_lfo];
			if(lfo_fn_table_index_offset){
				block_fnum += lfo_fn_table_index_offset;
				block = (block_fnum&0x1c00) >> 10;
				op->Cnt += (fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
			}else
				op->Cnt += op->Incr;
		}else
			op->Cnt += op->Incr;
	}
	noise_p += noise_f;
	i = noise_p >> FREQ_SH;
	noise_p &= FREQ_MASK;
	while (i){
		if(noise_rng & 1) noise_rng ^= 0x800302;
		noise_rng >>= 1;
		i--;
	}
}

static int
op_calc(u32int phase, uint env, int pm, uint wave_tab)
{
	u32int p;

	p = (env<<4) + sin_tab[wave_tab + ((((int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH) & SIN_MASK)];
	if(p >= TL_TAB_LEN)
		return 0;
	return tl_tab[p];
}

static int
op_calc1(u32int phase, uint env, int pm, uint wave_tab)
{
	u32int p;

	p = (env<<4) + sin_tab[wave_tab + ((((int)((phase & ~FREQ_MASK) + pm))>>FREQ_SH) & SIN_MASK)];
	if(p >= TL_TAB_LEN)
		return 0;
	return tl_tab[p];
}

#define volume_calc(OP) ((OP)->TLL + ((u32int)(OP)->volume) + (LFO_AM & (OP)->AMmask))

static void
chan_calc(Chan *CH)
{
	Op *SLOT;
	uint env;
	int out;

	phase_modulation = 0;
	phase_modulation2= 0;
	SLOT = &CH->SLOT[0];
	env = volume_calc(SLOT);
	out = SLOT->op1_out[0] + SLOT->op1_out[1];
	SLOT->op1_out[0] = SLOT->op1_out[1];
	SLOT->op1_out[1] = 0;
	if(env < ENV_QUIET){
		if(!SLOT->FB)
			out = 0;
		SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable);
	}
	*SLOT->connect += SLOT->op1_out[1];
	SLOT++;
	env = volume_calc(SLOT);
	if(env < ENV_QUIET)
		*SLOT->connect += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable);
}

static void
chan_calc_ext(Chan *CH)
{
	Op *SLOT;
	uint env;

	phase_modulation = 0;
	SLOT = &CH->SLOT[0];
	env = volume_calc(SLOT);
	if(env < ENV_QUIET)
		*SLOT->connect += op_calc(SLOT->Cnt, env, phase_modulation2, SLOT->wavetable);
	SLOT++;
	env = volume_calc(SLOT);
	if(env < ENV_QUIET)
		*SLOT->connect += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable);
}

static void
chan_calc_rhythm(Chan *CH, uint noise)
{
	Op *SLOT;
	int out;
	uint env;

	phase_modulation = 0;
	SLOT = &CH[6].SLOT[0];
	env = volume_calc(SLOT);
	out = SLOT->op1_out[0] + SLOT->op1_out[1];
	SLOT->op1_out[0] = SLOT->op1_out[1];
	if(!SLOT->CON)
		phase_modulation = SLOT->op1_out[0];
	SLOT->op1_out[1] = 0;
	if(env < ENV_QUIET){
		if(!SLOT->FB)
			out = 0;
		SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable);
	}
	SLOT++;
	env = volume_calc(SLOT);
	if(env < ENV_QUIET)
		chanout[6] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2;
	env = volume_calc(SLOT7_1);
	if(env < ENV_QUIET){
		uchar bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
		uchar bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
		uchar bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
		uchar res1 = (bit2 ^ bit7) | bit3;
		u32int phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
		uchar bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
		uchar bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
		uchar res2 = (bit3e ^ bit5e);
		if(res2)
			phase = (0x200|(0xd0>>2));
		if(phase&0x200){
			if(noise)
				phase = 0x200|0xd0;
		}else
		{
			if(noise)
				phase = 0xd0>>2;
		}
		chanout[7] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
	}
	env = volume_calc(SLOT7_2);
	if(env < ENV_QUIET){
		uchar bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1;
		u32int phase = bit8 ? 0x200 : 0x100;
		if(noise)
			phase ^= 0x100;
		chanout[7] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
	}
	env = volume_calc(SLOT8_1);
	if(env < ENV_QUIET)
		chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;
	env = volume_calc(SLOT8_2);
	if(env < ENV_QUIET){
		uchar bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
		uchar bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
		uchar bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
		uchar res1 = (bit2 ^ bit7) | bit3;
		u32int phase = res1 ? 0x300 : 0x100;
		uchar bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
		uchar bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
		uchar res2 = (bit3e ^ bit5e);
		if(res2)
			phase = 0x300;
		chanout[8] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
	}
}

static void
FM_KEYON(Op *SLOT, u32int key_set)
{
	if(!SLOT->key){
		SLOT->Cnt = 0;
		SLOT->state = EG_ATT;
	}
	SLOT->key |= key_set;
}

static void
FM_KEYOFF(Op *SLOT, u32int key_clr)
{
	if(SLOT->key){
		SLOT->key &= key_clr;
		if(!SLOT->key){
			if(SLOT->state>EG_REL)
				SLOT->state = EG_REL;
		}
	}
}

/* update phase increment counter of operator (also update the EG rates if necessary) */
static void
CALC_FCSLOT(Chan *CH, Op *SLOT)
{
	int ksr;

	SLOT->Incr = CH->fc * SLOT->mul;
	ksr = CH->kcode >> SLOT->KSR;
	if(SLOT->ksr != ksr){
		SLOT->ksr = ksr;
		if((SLOT->ar + SLOT->ksr) < 16+60){
			SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr];
			SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
			SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr];
		}else{
			SLOT->eg_sh_ar = 0;
			SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
			SLOT->eg_sel_ar = 13*RATE_STEPS;
		}
		SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr];
		SLOT->eg_m_dr = (1<<SLOT->eg_sh_dr)-1;
		SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr];
		SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
		SLOT->eg_m_rr = (1<<SLOT->eg_sh_rr)-1;
		SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr];
	}
}

static void
set_mul(int slot, int v)
{
	Chan *CH = &chs[slot/2];
	Op *SLOT = &CH->SLOT[slot&1];

	SLOT->mul = mul_tab[v&0x0f];
	SLOT->KSR = (v&0x10) ? 0 : 2;
	SLOT->eg_type = (v&0x20);
	SLOT->vib = (v&0x40);
	SLOT->AMmask = (v&0x80) ? ~0 : 0;
	if(OPL3_mode & 1){
		int chan_no = slot/2;
		switch(chan_no){
		case 0: case 1: case 2: case 9: case 10: case 11:
			CALC_FCSLOT(CH,SLOT);
			break;
		case 3: case 4: case 5: case 12: case 13: case 14:
			if((CH-3)->extended)
				CALC_FCSLOT(CH-3,SLOT);
			else
				CALC_FCSLOT(CH,SLOT);
			break;
		default:
			CALC_FCSLOT(CH,SLOT);
			break;
		}
	}else{
		CALC_FCSLOT(CH,SLOT);
	}
}

static void
set_ksl_tl(int slot, int v)
{
	Chan *CH = &chs[slot/2];
	Op *SLOT = &CH->SLOT[slot&1];

	SLOT->ksl = ksl_shift[v >> 6];
	SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
	if(OPL3_mode & 1){
		int chan_no = slot/2;
		switch(chan_no){
		case 0: case 1: case 2: case 9: case 10: case 11:
			SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
			break;
		case 3: case 4: case 5: case 12: case 13: case 14:
			if((CH-3)->extended)
				SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl);
			else
				SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
			break;
		default:
			SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
			break;
		}
	}else
		SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
}

static void
set_ar_dr(int slot, int v)
{
	Chan *CH = &chs[slot/2];
	Op *SLOT = &CH->SLOT[slot&1];

	SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
	if((SLOT->ar + SLOT->ksr) < 16+60){
		SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr];
		SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
		SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr];
	}else{
		SLOT->eg_sh_ar = 0;
		SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
		SLOT->eg_sel_ar = 13*RATE_STEPS;
	}
	SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
	SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr];
	SLOT->eg_m_dr = (1<<SLOT->eg_sh_dr)-1;
	SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr];
}

static void
set_sl_rr(int slot, int v)
{
	Chan *CH = &chs[slot/2];
	Op *SLOT = &CH->SLOT[slot&1];

	SLOT->sl = sl_tab[v>>4];
	SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
	SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
	SLOT->eg_m_rr = (1<<SLOT->eg_sh_rr)-1;
	SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr];
}

void
opl3wr(int r, int v)
{
	Chan *CH;
	uint ch_offset = 0;
	int slot;
	int block_fnum;

	v &= 0xff;
	if(r&0x100){
		switch(r){
		case 0x101:
			return;
		case 0x104:
			CH = &chs[0];
			CH->extended = (v>>0) & 1;
			CH++;
			CH->extended = (v>>1) & 1;
			CH++;
			CH->extended = (v>>2) & 1;
			CH = &chs[9];
			CH->extended = (v>>3) & 1;
			CH++;
			CH->extended = (v>>4) & 1;
			CH++;
			CH->extended = (v>>5) & 1;
			return;
		case 0x105:
			OPL3_mode = v & 1;
			return;
		}
		ch_offset = 9;
	}
	r &= 0xff;
	v &= 0xff;
	switch(r&0xe0){
	case 0x00:
		switch(r&0x1f){
		case 0x08:
			nts = v;
			break;
		}
		break;
	case 0x20:
		slot = slot_array[r&0x1f];
		if(slot < 0) return;
		set_mul(slot + ch_offset*2, v);
		break;
	case 0x40:
		slot = slot_array[r&0x1f];
		if(slot < 0) return;
		set_ksl_tl(slot + ch_offset*2, v);
		break;
	case 0x60:
		slot = slot_array[r&0x1f];
		if(slot < 0) return;
		set_ar_dr(slot + ch_offset*2, v);
		break;
	case 0x80:
		slot = slot_array[r&0x1f];
		if(slot < 0) return;
		set_sl_rr(slot + ch_offset*2, v);
		break;
	case 0xa0:
		if(r == 0xbd){
			if(ch_offset != 0)
				return;
			lfo_am_depth = v & 0x80;
			lfo_pm_depth_range = (v&0x40) ? 8 : 0;
			rhythm = v & 0x3f;
			if(rhythm & 0x20){
				if(v&0x10){
					FM_KEYON (&chs[6].SLOT[0], 2);
					FM_KEYON (&chs[6].SLOT[1], 2);
				}else{
					FM_KEYOFF(&chs[6].SLOT[0],~2);
					FM_KEYOFF(&chs[6].SLOT[1],~2);
				}
				if(v&0x01) FM_KEYON (&chs[7].SLOT[0], 2);
				else FM_KEYOFF(&chs[7].SLOT[0],~2);
				if(v&0x08) FM_KEYON (&chs[7].SLOT[1], 2);
				else FM_KEYOFF(&chs[7].SLOT[1],~2);
				if(v&0x04) FM_KEYON (&chs[8].SLOT[0], 2);
				else FM_KEYOFF(&chs[8].SLOT[0],~2);
				if(v&0x02) FM_KEYON (&chs[8].SLOT[1], 2);
				else FM_KEYOFF(&chs[8].SLOT[1],~2);
			}else{
				FM_KEYOFF(&chs[6].SLOT[0],~2);
				FM_KEYOFF(&chs[6].SLOT[1],~2);
				FM_KEYOFF(&chs[7].SLOT[0],~2);
				FM_KEYOFF(&chs[7].SLOT[1],~2);
				FM_KEYOFF(&chs[8].SLOT[0],~2);
				FM_KEYOFF(&chs[8].SLOT[1],~2);
			}
			return;
		}
		if((r&0x0f) > 8) return;
		CH = &chs[(r&0x0f) + ch_offset];
		if(!(r&0x10)){
			block_fnum = (CH->block_fnum&0x1f00) | v;
		}else{
			block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
			if(OPL3_mode & 1){
				int chan_no = (r&0x0f) + ch_offset;
				switch(chan_no){
				case 0: case 1: case 2: case 9: case 10: case 11:
					if(CH->extended){
						if(v&0x20){
							FM_KEYON (&CH->SLOT[0], 1);
							FM_KEYON (&CH->SLOT[1], 1);
							FM_KEYON (&(CH+3)->SLOT[0], 1);
							FM_KEYON (&(CH+3)->SLOT[1], 1);
						}else{
							FM_KEYOFF(&CH->SLOT[0],~1);
							FM_KEYOFF(&CH->SLOT[1],~1);
							FM_KEYOFF(&(CH+3)->SLOT[0],~1);
							FM_KEYOFF(&(CH+3)->SLOT[1],~1);
						}
					}else{
						if(v&0x20){
							FM_KEYON (&CH->SLOT[0], 1);
							FM_KEYON (&CH->SLOT[1], 1);
						}else{
							FM_KEYOFF(&CH->SLOT[0],~1);
							FM_KEYOFF(&CH->SLOT[1],~1);
						}
					}
					break;
				case 3: case 4: case 5: case 12: case 13: case 14:
					if((CH-3)->extended){
					}else{
						if(v&0x20){
							FM_KEYON (&CH->SLOT[0], 1);
							FM_KEYON (&CH->SLOT[1], 1);
						}else{
							FM_KEYOFF(&CH->SLOT[0],~1);
							FM_KEYOFF(&CH->SLOT[1],~1);
						}
					}
					break;
				default:
					if(v&0x20){
						FM_KEYON (&CH->SLOT[0], 1);
						FM_KEYON (&CH->SLOT[1], 1);
					}else{
						FM_KEYOFF(&CH->SLOT[0],~1);
						FM_KEYOFF(&CH->SLOT[1],~1);
					}
					break;
				}
			}else{
				if(v&0x20){
					FM_KEYON (&CH->SLOT[0], 1);
					FM_KEYON (&CH->SLOT[1], 1);
				}else{
					FM_KEYOFF(&CH->SLOT[0],~1);
					FM_KEYOFF(&CH->SLOT[1],~1);
				}
			}
		}
		if(CH->block_fnum != block_fnum){
			u8int block = block_fnum >> 10;
			CH->block_fnum = block_fnum;
			CH->ksl_base = ksl_tab[block_fnum>>6];
			CH->fc = fn_tab[block_fnum&0x03ff] >> (7-block);
			CH->kcode = (CH->block_fnum&0x1c00)>>9;
			if(nts&0x40)
				CH->kcode |= (CH->block_fnum&0x100)>>8;
			else
				CH->kcode |= (CH->block_fnum&0x200)>>9;
			if(OPL3_mode & 1){
				int chan_no = (r&0x0f) + ch_offset;
				switch(chan_no){
				case 0: case 1: case 2: case 9: case 10: case 11:
					if(CH->extended){
						CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl);
						CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl);
						(CH+3)->SLOT[0].TLL = (CH+3)->SLOT[0].TL + (CH->ksl_base>>(CH+3)->SLOT[0].ksl);
						(CH+3)->SLOT[1].TLL = (CH+3)->SLOT[1].TL + (CH->ksl_base>>(CH+3)->SLOT[1].ksl);
						CALC_FCSLOT(CH,&CH->SLOT[0]);
						CALC_FCSLOT(CH,&CH->SLOT[1]);
						CALC_FCSLOT(CH,&(CH+3)->SLOT[0]);
						CALC_FCSLOT(CH,&(CH+3)->SLOT[1]);
					}else{
						CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl);
						CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl);
						CALC_FCSLOT(CH,&CH->SLOT[0]);
						CALC_FCSLOT(CH,&CH->SLOT[1]);
					}
					break;
				case 3: case 4: case 5: case 12: case 13: case 14:
					if((CH-3)->extended){
					}else{
						CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl);
						CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl);
						CALC_FCSLOT(CH,&CH->SLOT[0]);
						CALC_FCSLOT(CH,&CH->SLOT[1]);
					}
					break;
				default:
					CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl);
					CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl);
					CALC_FCSLOT(CH,&CH->SLOT[0]);
					CALC_FCSLOT(CH,&CH->SLOT[1]);
					break;
				}
			}else{
				CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl);
				CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl);
				CALC_FCSLOT(CH,&CH->SLOT[0]);
				CALC_FCSLOT(CH,&CH->SLOT[1]);
			}
		}
		break;
	case 0xc0:
		if((r&0xf) > 8) return;
		CH = &chs[(r&0xf) + ch_offset];
		if(OPL3_mode & 1){
			int base = ((r&0xf) + ch_offset) * 4;
			pan[base] = (v & 0x10) ? ~0 : 0;
			pan[base +1] = (v & 0x20) ? ~0 : 0;
			pan[base +2] = (v & 0x40) ? ~0 : 0;
			pan[base +3] = (v & 0x80) ? ~0 : 0;
		}else{
			int base = ((r&0xf) + ch_offset) * 4;
			pan[base] = ~0;
			pan[base +1] = ~0;
			pan[base +2] = ~0;
			pan[base +3] = ~0;
		}
		pan_ctrl_value[(r&0xf) + ch_offset] = v;
		CH->SLOT[0].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
		CH->SLOT[0].CON = v&1;
		if(OPL3_mode & 1){
			int chan_no = (r&0x0f) + ch_offset;
			switch(chan_no){
			case 0: case 1: case 2: case 9: case 10: case 11:
				if(CH->extended){
					u8int conn = (CH->SLOT[0].CON<<1) | ((CH+3)->SLOT[0].CON<<0);
					switch(conn){
					case 0:
						CH->SLOT[0].connect = &phase_modulation;
						CH->SLOT[1].connect = &phase_modulation2;
						(CH+3)->SLOT[0].connect = &phase_modulation;
						(CH+3)->SLOT[1].connect = &chanout[chan_no + 3];
						break;
					case 1:
						CH->SLOT[0].connect = &phase_modulation;
						CH->SLOT[1].connect = &chanout[chan_no];
						(CH+3)->SLOT[0].connect = &phase_modulation;
						(CH+3)->SLOT[1].connect = &chanout[chan_no + 3];
						break;
					case 2:
						CH->SLOT[0].connect = &chanout[chan_no];
						CH->SLOT[1].connect = &phase_modulation2;
						(CH+3)->SLOT[0].connect = &phase_modulation;
						(CH+3)->SLOT[1].connect = &chanout[chan_no + 3];
						break;
					case 3:
						CH->SLOT[0].connect = &chanout[chan_no];
						CH->SLOT[1].connect = &phase_modulation2;
						(CH+3)->SLOT[0].connect = &chanout[chan_no + 3];
						(CH+3)->SLOT[1].connect = &chanout[chan_no + 3];
						break;
					}
				}else{
					CH->SLOT[0].connect = CH->SLOT[0].CON ? &chanout[(r&0xf)+ch_offset] : &phase_modulation;
					CH->SLOT[1].connect = &chanout[(r&0xf)+ch_offset];
				}
				break;
			case 3: case 4: case 5: case 12: case 13: case 14:
				if((CH-3)->extended){
					u8int conn = ((CH-3)->SLOT[0].CON<<1) | (CH->SLOT[0].CON<<0);
					switch(conn){
					case 0:
						(CH-3)->SLOT[0].connect = &phase_modulation;
						(CH-3)->SLOT[1].connect = &phase_modulation2;
						CH->SLOT[0].connect = &phase_modulation;
						CH->SLOT[1].connect = &chanout[chan_no];
						break;
					case 1:
						(CH-3)->SLOT[0].connect = &phase_modulation;
						(CH-3)->SLOT[1].connect = &chanout[chan_no - 3];
						CH->SLOT[0].connect = &phase_modulation;
						CH->SLOT[1].connect = &chanout[chan_no];
						break;
					case 2:
						(CH-3)->SLOT[0].connect = &chanout[chan_no - 3];
						(CH-3)->SLOT[1].connect = &phase_modulation2;
						CH->SLOT[0].connect = &phase_modulation;
						CH->SLOT[1].connect = &chanout[chan_no];
						break;
					case 3:
						(CH-3)->SLOT[0].connect = &chanout[chan_no - 3];
						(CH-3)->SLOT[1].connect = &phase_modulation2;
						CH->SLOT[0].connect = &chanout[chan_no];
						CH->SLOT[1].connect = &chanout[chan_no];
						break;
					}
				}else{
					CH->SLOT[0].connect = CH->SLOT[0].CON ? &chanout[(r&0xf)+ch_offset] : &phase_modulation;
					CH->SLOT[1].connect = &chanout[(r&0xf)+ch_offset];
				}
				break;
			default:
					CH->SLOT[0].connect = CH->SLOT[0].CON ? &chanout[(r&0xf)+ch_offset] : &phase_modulation;
					CH->SLOT[1].connect = &chanout[(r&0xf)+ch_offset];
				break;
			}
		}else{
			CH->SLOT[0].connect = CH->SLOT[0].CON ? &chanout[(r&0xf)+ch_offset] : &phase_modulation;
			CH->SLOT[1].connect = &chanout[(r&0xf)+ch_offset];
		}
		break;
	case 0xe0:
		slot = slot_array[r&0x1f];
		if(slot < 0) return;
		slot += ch_offset*2;
		CH = &chs[slot/2];
		v &= 7;
		CH->SLOT[slot&1].waveform_number = v;
		if(!(OPL3_mode & 1))
			v &= 3;
		CH->SLOT[slot&1].wavetable = v * SIN_LEN;
		break;
	}
}

void
opl3out(uchar *p, int n)
{
	uchar *e;

	for(e=p+n; p<e; p+=4){
		int a,b;
		advance_lfo();
		memset(chanout, 0, sizeof(chanout));
		chan_calc(&chs[0]);
		if(chs[0].extended)
			chan_calc_ext(&chs[3]);
		else
			chan_calc(&chs[3]);
		chan_calc(&chs[1]);
		if(chs[1].extended)
			chan_calc_ext(&chs[4]);
		else
			chan_calc(&chs[4]);
		chan_calc(&chs[2]);
		if(chs[2].extended)
			chan_calc_ext(&chs[5]);
		else
			chan_calc(&chs[5]);
		if((rhythm & 0x20) == 0){
			chan_calc(&chs[6]);
			chan_calc(&chs[7]);
			chan_calc(&chs[8]);
		}else
			chan_calc_rhythm(&chs[0], (noise_rng>>0)&1);
		chan_calc(&chs[9]);
		if(chs[9].extended)
			chan_calc_ext(&chs[12]);
		else
			chan_calc(&chs[12]);
		chan_calc(&chs[10]);
		if(chs[10].extended)
			chan_calc_ext(&chs[13]);
		else
			chan_calc(&chs[13]);
		chan_calc(&chs[11]);
		if(chs[11].extended)
			chan_calc_ext(&chs[14]);
		else
			chan_calc(&chs[14]);
		chan_calc(&chs[15]);
		chan_calc(&chs[16]);
		chan_calc(&chs[17]);
		a = chanout[0] & pan[0];
		b = chanout[0] & pan[1];
		a += chanout[1] & pan[4];
		b += chanout[1] & pan[5];
		a += chanout[2] & pan[8];
		b += chanout[2] & pan[9];
		a += chanout[3] & pan[12];
		b += chanout[3] & pan[13];
		a += chanout[4] & pan[16];
		b += chanout[4] & pan[17];
		a += chanout[5] & pan[20];
		b += chanout[5] & pan[21];
		a += chanout[6] & pan[24];
		b += chanout[6] & pan[25];
		a += chanout[7] & pan[28];
		b += chanout[7] & pan[29];
		a += chanout[8] & pan[32];
		b += chanout[8] & pan[33];
		a += chanout[9] & pan[36];
		b += chanout[9] & pan[37];
		a += chanout[10] & pan[40];
		b += chanout[10] & pan[41];
		a += chanout[11] & pan[44];
		b += chanout[11] & pan[45];
		a += chanout[12] & pan[48];
		b += chanout[12] & pan[49];
		a += chanout[13] & pan[52];
		b += chanout[13] & pan[53];
		a += chanout[14] & pan[56];
		b += chanout[14] & pan[57];
		a += chanout[15] & pan[60];
		b += chanout[15] & pan[61];
		a += chanout[16] & pan[64];
		b += chanout[16] & pan[65];
		a += chanout[17] & pan[68];
		b += chanout[17] & pan[69];
		if(a > 32767)
			a = 32767;
		else if(a < -32768)
			a = -32768;
		if(b > 32767)
			b = 32767;
		else if(b < -32768)
			b = -32768;
		p[0] = a;
		p[1] = a >> 8;
		p[2] = b;
		p[3] = b >> 8;
		advance();
	}
}

static int
init_tables(void)
{
	int i, x, n;
	double o, m;

	for (x=0; x<TL_RES_LEN; x++){
		m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
		m = floor(m);
		n = (int)m;
		n >>= 4;
		if(n&1)
			n = (n>>1)+1;
		else
			n = n>>1;
		n <<= 1;
		tl_tab[x*2 + 0] = n;
		tl_tab[x*2 + 1] = ~tl_tab[x*2 + 0];
		for (i=1; i<13; i++){
			tl_tab[x*2+0 + i*2*TL_RES_LEN] = tl_tab[x*2+0]>>i;
			tl_tab[x*2+1 + i*2*TL_RES_LEN] = ~tl_tab[x*2+0 + i*2*TL_RES_LEN];
		}
	}
	for (i=0; i<SIN_LEN; i++){
		m = sin(((i*2)+1) * PI / SIN_LEN);
		if(m>0.0)
			o = 8*log(1.0/m)/log(2.0);
		else
			o = 8*log(-1.0/m)/log(2.0);
		o = o / (ENV_STEP/4);
		n = (int)(2.0*o);
		if(n&1)
			n = (n>>1)+1;
		else
			n = n>>1;
		sin_tab[i] = n*2 + (m>=0.0? 0: 1);
	}
	for (i=0; i<SIN_LEN; i++){
		if(i & (1<<(SIN_BITS-1)))
			sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
		else
			sin_tab[1*SIN_LEN+i] = sin_tab[i];
		sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1)];
		if(i & (1<<(SIN_BITS-2)))
			sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
		else
			sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
		if(i & (1<<(SIN_BITS-1)))
			sin_tab[4*SIN_LEN+i] = TL_TAB_LEN;
		else
			sin_tab[4*SIN_LEN+i] = sin_tab[i*2];
		if(i & (1<<(SIN_BITS-1)))
			sin_tab[5*SIN_LEN+i] = TL_TAB_LEN;
		else
			sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1)];
		if(i & (1<<(SIN_BITS-1)))
			sin_tab[6*SIN_LEN+i] = 1;
		else
			sin_tab[6*SIN_LEN+i] = 0;
		if(i & (1<<(SIN_BITS-1)))
			x = ((SIN_LEN-1)-i)*16 + 1;
		else
			x = i*16;
		if(x > TL_TAB_LEN)
			x = TL_TAB_LEN;
		sin_tab[7*SIN_LEN+i] = x;
	}
	return 1;
}

void
opl3init(int rate)
{
	int i, o;
	double f0;

	init_tables();
	f0 = (Clk / (8.0*36)) / rate;
	for(i=0 ; i < 1024 ; i++)
		fn_tab[i] = (u32int)((double)i * 64 * f0 * (1<<(FREQ_SH-10)));
	lfo_am_inc = (1.0 / 64.0) * (1<<LFO_SH) * f0;
	lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * f0;
	noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * f0;
	eg_timer_add = (1<<EG_SH) * f0;
	eg_timer_overflow = (1) * (1<<EG_SH);
	noise_rng = 1;
	for(i=0xff; i>=0x20; i--)
		opl3wr(i, 0);
	for(i=0x1ff; i>=0x120; i--)
		opl3wr(i, 0);
	for(i=0; i<9*2; i++){
		Chan *CH = &chs[i];
		for(o=0; o<2; o++){
			CH->SLOT[o].state = EG_OFF;
			CH->SLOT[o].volume = MAX_ATT_INDEX;
		}
	}
}