package audio.gme;
/**
*
* Test port of Gens YM2612 core.
* Stephan Dittrich, 2005
*
*/
public final class YM2612
{
static final int NULL_RATE_SIZE = 32;
// YM2612 Hardware
private final class cSlot {
int[] DT;
int MUL;
int TL;
int TLL;
int SLL;
int KSR_S;
int KSR;
int SEG;
int AR;
int DR;
int SR;
int RR;
int Fcnt;
int Finc;
int Ecurp;
int Ecnt;
int Einc;
int Ecmp;
int EincA;
int EincD;
int EincS;
int EincR;
int INd;
int ChgEnM;
int AMS;
int AMSon;
};
private final class cChannel {
final int[] S0_OUT = new int[4];
int Old_OUTd;
int OUTd;
int LEFT;
int RIGHT;
int ALGO;
int FB;
int FMS;
int AMS;
final int[] FNUM = new int[4];
final int[] FOCT = new int[4];
final int[] KC = new int[4];
final cSlot[] SLOT = new cSlot[4];
int FFlag;
public cChannel(){
for(int i=0; i<4; i++) SLOT[i] = new cSlot();
}
};
private final class cYM2612 {
int Clock;
int Rate;
int TimerBase;
int Status;
int LFOcnt;
int LFOinc;
int TimerA;
int TimerAL;
int TimerAcnt;
int TimerB;
int TimerBL;
int TimerBcnt;
int Mode;
int DAC;
double Frequency;
long Inter_Cnt; // UINT
long Inter_Step; // UINT
final cChannel[] CHANNEL = new cChannel[6];
final int[][] REG = new int[2][0x100];
public cYM2612(){
for(int i=0; i<6; i++) CHANNEL[i] = new cChannel();
}
};
// Constants ( taken from MAME YM2612 core )
private static final int UPD_SIZE = 4000;
private static final int OUTP_BITS = 16;
private static final double PI = Math.PI;
private static final int ATTACK = 0;
private static final int DECAY = 1;
private static final int SUSTAIN = 2;
private static final int RELEASE = 3;
private static final int SIN_HBITS = 12;
private static final int SIN_LBITS = ((26-SIN_HBITS)<=16)?(26-SIN_HBITS):16;
private static final int ENV_HBITS = 12;
private static final int ENV_LBITS = (28 - ENV_HBITS);
private static final int LFO_HBITS = 10;
private static final int LFO_LBITS = (28 - LFO_HBITS);
private static final int SINLEN = (1 << SIN_HBITS);
private static final int ENVLEN = (1 << ENV_HBITS);
private static final int LFOLEN = (1 << LFO_HBITS);
private static final int TLLEN = (ENVLEN * 3);
private static final int SIN_MSK = (SINLEN - 1);
private static final int ENV_MSK = (ENVLEN - 1);
private static final int LFO_MSK = (LFOLEN - 1);
private static final double ENV_STEP = (96.0 / ENVLEN);
private static final int ENV_ATTACK = ((ENVLEN * 0) << ENV_LBITS);
private static final int ENV_DECAY = ((ENVLEN * 1) << ENV_LBITS);
private static final int ENV_END = ((ENVLEN * 2) << ENV_LBITS);
private static final int MAX_OUT_BITS = (SIN_HBITS + SIN_LBITS + 2);
private static final int MAX_OUT = ((1 << MAX_OUT_BITS) - 1);
private static final int OUT_BITS = (OUTP_BITS - 2);
private static final int FINAL_SHFT = (MAX_OUT_BITS - OUT_BITS) + 1;
private static final int LIMIT_CH_OUT = ((int) (((1 << OUT_BITS) * 1.5) - 1));
private static final int PG_CUT_OFF = ((int) (78.0 / ENV_STEP));
// private static final int ENV_CUT_OFF = ((int) (68.0 / ENV_STEP));
private static final int AR_RATE = 399128;
private static final int DR_RATE = 5514396;
private static final int LFO_FMS_LBITS = 9;
private static final int LFO_FMS_BASE = ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS)));
private static final int S0 = 0;
private static final int S1 = 2;
private static final int S2 = 1;
private static final int S3 = 3;
private static final int[] DT_DEF_TAB = {
// FD = 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,
// FD = 1
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
// FD = 2
1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16,
// FD = 3
2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22
};
private static final int[] FKEY_TAB = {
0, 0, 0, 0,
0, 0, 0, 1,
2, 3, 3, 3,
3, 3, 3, 3
};
private static final int[] LFO_AMS_TAB = {
31, 4, 1, 0
};
private static final int[] LFO_FMS_TAB = {
LFO_FMS_BASE * 0, LFO_FMS_BASE * 1,
LFO_FMS_BASE * 2, LFO_FMS_BASE * 3,
LFO_FMS_BASE * 4, LFO_FMS_BASE * 6,
LFO_FMS_BASE * 12, LFO_FMS_BASE * 24
};
// Variables
private final int[] SIN_TAB = new int[SINLEN];
private final int[] TL_TAB = new int[TLLEN*2];
private final int[] ENV_TAB = new int[2*ENVLEN+8]; // uint
private final int[] DECAY_TO_ATTACK = new int[ENVLEN]; // uint
private final int[] FINC_TAB = new int[2048]; // uint
static final int AR_NULL_RATE = 128;
private final int[] AR_TAB = new int[AR_NULL_RATE + NULL_RATE_SIZE]; // uint
static final int DR_NULL_RATE = 96;
private final int[] DR_TAB = new int[DR_NULL_RATE + NULL_RATE_SIZE]; // uint
private final int[][] DT_TAB = new int[8][32]; // uint
private final int[] SL_TAB = new int[16]; // uint
private final int[] LFO_ENV_TAB = new int[LFOLEN];
private final int[] LFO_FREQ_TAB = new int[LFOLEN];
private final int[] LFO_ENV_UP = new int[UPD_SIZE];
private final int[] LFO_FREQ_UP = new int[UPD_SIZE];
private final int[] LFO_INC_TAB = new int[8];
private int in0, in1, in2, in3;
private int en0, en1, en2, en3;
private int int_cnt;
// Emultaion State
private boolean EnableSSGEG = false;
private static final int MAIN_SHIFT = FINAL_SHFT;
int YM2612_Clock;
int YM2612_Rate;
int YM2612_TimerBase;
int YM2612_Status;
int YM2612_LFOcnt;
int YM2612_LFOinc;
int YM2612_TimerA;
int YM2612_TimerAL;
int YM2612_TimerAcnt;
int YM2612_TimerB;
int YM2612_TimerBL;
int YM2612_TimerBcnt;
int YM2612_Mode;
int YM2612_DAC;
double YM2612_Frequency;
long YM2612_Inter_Cnt; // UINT
long YM2612_Inter_Step; // UINT
final cChannel[] YM2612_CHANNEL = new cChannel[6];
final int[][] YM2612_REG = new int[2][0x100];
/**
* Creates a new instance of YM2612
*/
public YM2612() {
for(int i=0; i<6; i++) YM2612_CHANNEL[i] = new cChannel();
}
// YM2612 Emulation Methods
/***********************************************
*
* Public Access
*
***********************************************/
static private double log10( double x )
{
return Math.log( x ) / Math.log( 10.0 );
}
public final int init(int Clock, int Rate)
{
int i, j;
double x;
if((Rate == 0) || (Clock == 0)) return 1;
YM2612_Clock = Clock;
YM2612_Rate = Rate;
YM2612_Frequency = ((double) YM2612_Clock / (double) YM2612_Rate) / 144.0;
YM2612_TimerBase = (int) (YM2612_Frequency * 4096.0);
YM2612_Inter_Step = 0x4000;
YM2612_Inter_Cnt = 0;
// TL Table :
// [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0)
// [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0)
for(i = 0; i < TLLEN; i++){
if(i >= PG_CUT_OFF){
TL_TAB[TLLEN + i] = TL_TAB[i] = 0;
} else {
x = MAX_OUT; // Max output
x /= Math.pow(10, (ENV_STEP * i) / 20);
TL_TAB[i] = (int) x;
TL_TAB[TLLEN + i] = -TL_TAB[i];
}
}
// SIN Table :
// SIN_TAB[x][y] = sin(x) * y;
// x = phase and y = volume
SIN_TAB[0] = PG_CUT_OFF;
SIN_TAB[SINLEN/2] = PG_CUT_OFF;
for(i = 1; i <= SINLEN / 4; i++){
x = Math.sin(2.0 * PI * (double) (i) / (double) (SINLEN)); // Sinus
x = 20 * log10(1 / x); // convert to dB
j = (int) (x / ENV_STEP); // Get TL range
if(j > PG_CUT_OFF) j = (int) PG_CUT_OFF;
SIN_TAB[i] = j;
SIN_TAB[(SINLEN / 2) - i] = j;
SIN_TAB[(SINLEN / 2) + i] = TLLEN+j;
SIN_TAB[SINLEN - i] = TLLEN+j;
}
// LFO Table (LFO wav) :
for(i = 0; i < LFOLEN; i++){
x = Math.sin(2.0 * PI * (double) (i) / (double) (LFOLEN)); // Sinus
x += 1.0;
x /= 2.0;
x *= 11.8 / ENV_STEP;
LFO_ENV_TAB[i] = (int) x;
x = Math.sin(2.0 * PI * (double) (i) / (double) (LFOLEN)); // Sinus
x *= (double) ((1 << (LFO_HBITS - 1)) - 1);
LFO_FREQ_TAB[i] = (int) x;
}
for(i = 0; i < ENVLEN; i++){
x = Math.pow(((double) ((ENVLEN - 1) - i) / (double) (ENVLEN)), 8);
x *= ENVLEN;
ENV_TAB[i] = (int) x;
x = Math.pow(((double) (i) / (double) (ENVLEN)), 1);
x *= ENVLEN;
ENV_TAB[ENVLEN + i] = (int) x;
}
ENV_TAB[ENV_END >> ENV_LBITS] = ENVLEN - 1;
// Table Decay and Decay
for(i = 0, j = ENVLEN - 1; i < ENVLEN; i++){
while (j!=0 && (ENV_TAB[j] < i)) j--;
DECAY_TO_ATTACK[i] = j << ENV_LBITS;
}
// Sustain Level Table
for(i = 0; i < 15; i++){
x = i * 3;
x /= ENV_STEP;
j = (int) x;
j <<= ENV_LBITS;
SL_TAB[i] = j + ENV_DECAY;
}
j = ENVLEN - 1; // special case : volume off
j <<= ENV_LBITS;
SL_TAB[15] = j + ENV_DECAY;
//Frequency Step Table
for(i = 0; i < 2048; i++){
x = (double) i * YM2612_Frequency;
if((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0){
x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS));
} else {
x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7)));
}
x /= 2.0; // because MUL = value * 2
FINC_TAB[i] = (int) x; // (unsigned int) x;
}
// Attack & Decay Rate Table
for(i = 0; i < 4; i++){
AR_TAB[i] = 0;
DR_TAB[i] = 0;
}
for(i = 0; i < 60; i++){
x = YM2612_Frequency;
x *= 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75
x *= (double) (1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15)
x *= (double) (ENVLEN << ENV_LBITS); // on ajuste pour le tableau ENV_TAB
AR_TAB[i + 4] = (int) (x / AR_RATE); // (unsigned int) (x / AR_RATE);
DR_TAB[i + 4] = (int) (x / DR_RATE); // (unsigned int) (x / DR_RATE);
}
for(i = 64; i < 96; i++){
AR_TAB[i] = AR_TAB[63];
DR_TAB[i] = DR_TAB[63];
AR_TAB[i - 64+AR_NULL_RATE] = 0;
DR_TAB[i - 64+DR_NULL_RATE] = 0;
}
// Detune Table
for(i = 0; i < 4; i++){
for (j = 0; j < 32; j++){
if((SIN_LBITS + SIN_HBITS - 21) < 0){
x = (double) DT_DEF_TAB[(i << 5) + j] * YM2612_Frequency / (double) (1 << (21 - SIN_LBITS - SIN_HBITS));
} else {
x = (double) DT_DEF_TAB[(i << 5) + j] * YM2612_Frequency * (double) (1 << (SIN_LBITS + SIN_HBITS - 21));
}
DT_TAB[i + 0][j] = (int) x;
DT_TAB[i + 4][j] = (int) -x;
}
}
// LFO Table
j = (int)((YM2612_Rate * YM2612_Inter_Step) / 0x4000);
LFO_INC_TAB[0] = (int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
LFO_INC_TAB[1] = (int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
LFO_INC_TAB[2] = (int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
LFO_INC_TAB[3] = (int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
LFO_INC_TAB[4] = (int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
LFO_INC_TAB[5] = (int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
LFO_INC_TAB[6] = (int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
LFO_INC_TAB[7] = (int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j);
reset();
return 0;
}
public final int reset(){
int i, j;
YM2612_LFOcnt = 0;
YM2612_TimerA = 0;
YM2612_TimerAL = 0;
YM2612_TimerAcnt = 0;
YM2612_TimerB = 0;
YM2612_TimerBL = 0;
YM2612_TimerBcnt = 0;
YM2612_DAC = 0;
YM2612_Status = 0;
YM2612_Inter_Cnt = 0;
for(i = 0; i < 6; i++){
YM2612_CHANNEL[i].Old_OUTd = 0;
YM2612_CHANNEL[i].OUTd = 0;
YM2612_CHANNEL[i].LEFT = 0xFFFFFFFF;
YM2612_CHANNEL[i].RIGHT = 0xFFFFFFFF;
YM2612_CHANNEL[i].ALGO = 0;
YM2612_CHANNEL[i].FB = 31;
YM2612_CHANNEL[i].FMS = 0;
YM2612_CHANNEL[i].AMS = 0;
for(j = 0 ;j < 4 ; j++){
YM2612_CHANNEL[i].S0_OUT[j] = 0;
YM2612_CHANNEL[i].FNUM[j] = 0;
YM2612_CHANNEL[i].FOCT[j] = 0;
YM2612_CHANNEL[i].KC[j] = 0;
YM2612_CHANNEL[i].SLOT[j].Fcnt = 0;
YM2612_CHANNEL[i].SLOT[j].Finc = 0;
YM2612_CHANNEL[i].SLOT[j].Ecnt = ENV_END; // Put it at the end of Decay phase...
YM2612_CHANNEL[i].SLOT[j].Einc = 0;
YM2612_CHANNEL[i].SLOT[j].Ecmp = 0;
YM2612_CHANNEL[i].SLOT[j].Ecurp = RELEASE;
YM2612_CHANNEL[i].SLOT[j].ChgEnM = 0;
}
}
for(i = 0; i < 0x100; i++){
YM2612_REG[0][i] = -1;
YM2612_REG[1][i] = -1;
}
for(i = 0xB6; i >= 0xB4; i--){
write0(i, 0xC0);
write1(i, 0xC0);
}
for(i = 0xB2; i >= 0x22; i--){
write0(i, 0);
write1(i, 0);
}
write0(0x2A, 0x80);
return 0;
}
public final int read(){
return (YM2612_Status);
}
public final void write0( int addr, int data )
{
if ( addr < 0x30 )
{
YM2612_REG[0][addr] = data;
setYM(addr, data);
}
else if ( YM2612_REG[0][addr] != data )
{
YM2612_REG[0][addr] = data;
if(addr < 0xA0)
setSlot(addr, data);
else
setChannel(addr, data);
}
}
public final void write1( int addr, int data )
{
if ( addr >= 0x30 && YM2612_REG[1][addr] != data )
{
YM2612_REG[1][addr] = data;
if(addr < 0xA0)
setSlot(addr + 0x100, data);
else
setChannel(addr + 0x100, data);
}
}
public final void update(int[] buf_lr, int offset, int end)
{
offset *= 2;
end = end*2 + offset;
if(YM2612_CHANNEL[0].SLOT[0].Finc == -1) calc_FINC_CH(YM2612_CHANNEL[0]);
if(YM2612_CHANNEL[1].SLOT[0].Finc == -1) calc_FINC_CH(YM2612_CHANNEL[1]);
if(YM2612_CHANNEL[2].SLOT[0].Finc == -1)
{
if( (YM2612_Mode&0x40)!=0 )
{
calc_FINC_SL((YM2612_CHANNEL[2].SLOT[S0]), FINC_TAB[YM2612_CHANNEL[2].FNUM[2]] >> (7 - YM2612_CHANNEL[2].FOCT[2]), YM2612_CHANNEL[2].KC[2]);
calc_FINC_SL((YM2612_CHANNEL[2].SLOT[S1]), FINC_TAB[YM2612_CHANNEL[2].FNUM[3]] >> (7 - YM2612_CHANNEL[2].FOCT[3]), YM2612_CHANNEL[2].KC[3]);
calc_FINC_SL((YM2612_CHANNEL[2].SLOT[S2]), FINC_TAB[YM2612_CHANNEL[2].FNUM[1]] >> (7 - YM2612_CHANNEL[2].FOCT[1]), YM2612_CHANNEL[2].KC[1]);
calc_FINC_SL((YM2612_CHANNEL[2].SLOT[S3]), FINC_TAB[YM2612_CHANNEL[2].FNUM[0]] >> (7 - YM2612_CHANNEL[2].FOCT[0]), YM2612_CHANNEL[2].KC[0]);
}
else
{
calc_FINC_CH(YM2612_CHANNEL[2]);
}
}
if(YM2612_CHANNEL[3].SLOT[0].Finc == -1) calc_FINC_CH(YM2612_CHANNEL[3]);
if(YM2612_CHANNEL[4].SLOT[0].Finc == -1) calc_FINC_CH(YM2612_CHANNEL[4]);
if(YM2612_CHANNEL[5].SLOT[0].Finc == -1) calc_FINC_CH(YM2612_CHANNEL[5]);
// if(YM2612_Inter_Step & 0x04000) algo_type = 0;
// else algo_type = 16;
int algo_type = 0;
if( (YM2612_LFOinc)!=0 )
{
// Precalculate LFO wave
for ( int o = offset; o < end; o += 2 )
{
int i = o >> 1;
int j = ((YM2612_LFOcnt += YM2612_LFOinc) >> LFO_LBITS) & LFO_MSK;
LFO_ENV_UP[i] = LFO_ENV_TAB[j];
LFO_FREQ_UP[i] = LFO_FREQ_TAB[j];
}
algo_type |= 8;
}
updateChannel((YM2612_CHANNEL[0].ALGO + algo_type), (YM2612_CHANNEL[0]), buf_lr, offset, end);
updateChannel((YM2612_CHANNEL[1].ALGO + algo_type), (YM2612_CHANNEL[1]), buf_lr, offset, end);
updateChannel((YM2612_CHANNEL[2].ALGO + algo_type), (YM2612_CHANNEL[2]), buf_lr, offset, end);
updateChannel((YM2612_CHANNEL[3].ALGO + algo_type), (YM2612_CHANNEL[3]), buf_lr, offset, end);
updateChannel((YM2612_CHANNEL[4].ALGO + algo_type), (YM2612_CHANNEL[4]), buf_lr, offset, end);
if ( YM2612_DAC == 0 )
updateChannel( YM2612_CHANNEL[5].ALGO + algo_type, YM2612_CHANNEL[5], buf_lr, offset, end );
YM2612_Inter_Cnt = int_cnt;
}
public final void synchronizeTimers(int length){
int i;
i = YM2612_TimerBase * length;
if( (YM2612_Mode&1)!=0 ){
// if((YM2612_TimerAcnt -= 14073) <= 0){ // 13879=NTSC (old: 14475=NTSC 14586=PAL)
if((YM2612_TimerAcnt -= i) <= 0){
YM2612_Status |= (YM2612_Mode & 0x04) >> 2;
YM2612_TimerAcnt += YM2612_TimerAL;
if( (YM2612_Mode&0x80)!=0 ) CSM_Key_Control();
}
}
if( (YM2612_Mode&2)!=0 ){
// if((YM2612_TimerBcnt -= 14073) <= 0){ // 13879=NTSC (old: 14475=NTSC 14586=PAL)
if((YM2612_TimerBcnt -= i) <= 0){
YM2612_Status |= (YM2612_Mode & 0x08) >> 2;
YM2612_TimerBcnt += YM2612_TimerBL;
}
}
}
/***********************************************
*
* Parameter Calculation
*
***********************************************/
private final void calc_FINC_SL(cSlot SL, int finc, int kc){
int ksr;
SL.Finc = (finc + SL.DT [kc]) * SL.MUL;
ksr = kc >> SL.KSR_S;
if(SL.KSR != ksr){
SL.KSR = ksr;
SL.EincA = AR_TAB [SL.AR + ksr];
SL.EincD = DR_TAB [SL.DR + ksr];
SL.EincS = DR_TAB [SL.SR + ksr];
SL.EincR = DR_TAB [SL.RR + ksr];
if(SL.Ecurp == ATTACK) SL.Einc = SL.EincA;
else if(SL.Ecurp == DECAY) SL.Einc = SL.EincD;
else if(SL.Ecnt < ENV_END){
if(SL.Ecurp == SUSTAIN) SL.Einc = SL.EincS;
else if(SL.Ecurp == RELEASE) SL.Einc = SL.EincR;
}
}
}
private final void calc_FINC_CH(cChannel CH){
int finc, kc;
finc = (int)(FINC_TAB[CH.FNUM[0]] >> (7 - CH.FOCT[0]));
kc = CH.KC[0];
calc_FINC_SL(CH.SLOT[0], finc, kc);
calc_FINC_SL(CH.SLOT[1], finc, kc);
calc_FINC_SL(CH.SLOT[2], finc, kc);
calc_FINC_SL(CH.SLOT[3], finc, kc);
}
/***********************************************
*
* Settings
*
***********************************************/
private final void KEY_ON(cChannel CH, int nsl){
cSlot SL = CH.SLOT[nsl];
if(SL.Ecurp == RELEASE){
SL.Fcnt = 0;
// Fix Ecco 2 splash sound
SL.Ecnt = (DECAY_TO_ATTACK[ENV_TAB[SL.Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL.ChgEnM;
SL.ChgEnM = 0xFFFFFFFF;
SL.Einc = SL.EincA;
SL.Ecmp = ENV_DECAY;
SL.Ecurp = ATTACK;
}
}
private final void KEY_OFF(cChannel CH, int nsl){
cSlot SL = CH.SLOT[nsl];
if(SL.Ecurp != RELEASE){
if(SL.Ecnt < ENV_DECAY){
SL.Ecnt = (ENV_TAB[SL.Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY;
}
SL.Einc = SL.EincR;
SL.Ecmp = ENV_END;
SL.Ecurp = RELEASE;
}
}
private final void CSM_Key_Control(){
KEY_ON(YM2612_CHANNEL[2], 0);
KEY_ON(YM2612_CHANNEL[2], 1);
KEY_ON(YM2612_CHANNEL[2], 2);
KEY_ON(YM2612_CHANNEL[2], 3);
}
private final int setSlot(int address, int data){ // INT, UCHAR
data &= 0xFF; // unsign
cChannel CH;
cSlot SL;
int nch, nsl;
if((nch = address & 3) == 3) return 1;
nsl = (address >> 2) & 3;
if( (address&0x100)!=0 ) nch += 3;
CH = YM2612_CHANNEL[nch];
SL = CH.SLOT[nsl];
switch(address & 0xF0){
case 0x30:
if( (SL.MUL=(data&0x0F))!=0 ) SL.MUL <<= 1;
else SL.MUL = 1;
SL.DT = DT_TAB[(data >> 4) & 7]; // = DT_TAB[(data >> 4) & 7];
CH.SLOT[0].Finc = -1;
break;
case 0x40:
SL.TL = data & 0x7F;
// SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound...
if((ENV_HBITS - 7) < 0) SL.TLL = SL.TL >> (7 - ENV_HBITS);
else SL.TLL = SL.TL << (ENV_HBITS - 7);
break;
case 0x50:
SL.KSR_S = 3 - (data >> 6);
CH.SLOT[0].Finc = -1;
if( (data&=0x1F)!=0 ) SL.AR = data<<1; // = &AR_TAB[data << 1];
else SL.AR = AR_NULL_RATE; // &NULL_RATE[0];
SL.EincA = AR_TAB [SL.AR + SL.KSR]; // SL.AR[SL.KSR];
if(SL.Ecurp == ATTACK) SL.Einc = SL.EincA;
break;
case 0x60:
if( (SL.AMSon=(data&0x80))!=0 ) SL.AMS = CH.AMS;
else SL.AMS = 31;
if( (data&=0x1F)!=0) SL.DR = data<<1; // = &DR_TAB[data << 1];
else SL.DR = DR_NULL_RATE; // = &NULL_RATE[0];
SL.EincD = DR_TAB [SL.DR + SL.KSR]; // SL.DR[SL.KSR];
if(SL.Ecurp == DECAY) SL.Einc = SL.EincD;
break;
case 0x70:
if( (data&=0x1F)!=0) SL.SR = data<<1; // = &DR_TAB[data << 1];
else SL.SR = DR_NULL_RATE; // = &NULL_RATE[0];
SL.EincS = DR_TAB [SL.SR + SL.KSR];
if((SL.Ecurp == SUSTAIN) && (SL.Ecnt < ENV_END)) SL.Einc = SL.EincS;
break;
case 0x80:
SL.SLL = SL_TAB[data >> 4];
SL.RR = ((data&0xF)<<2)+2; // = &DR_TAB[((data & 0xF) << 2) + 2];
SL.EincR = DR_TAB [SL.RR + SL.KSR]; // [SL.KSR];
if((SL.Ecurp == RELEASE) && (SL.Ecnt < ENV_END)) SL.Einc = SL.EincR;
break;
case 0x90:
if(EnableSSGEG){
if( (data&0x08)!=0 ) SL.SEG = data&0x0F;
else SL.SEG = 0;
}
break;
}
return 0;
}
private final int setChannel(int address, int data){ // INT,UCHAR
data&=0xFF; // unsign
cChannel CH;
int num;
if((num = address & 3) == 3) return 1;
switch(address&0xFC){
case 0xA0:
if( (address&0x100)!=0 ) num += 3;
CH = YM2612_CHANNEL[num];
CH.FNUM[0] = (CH.FNUM[0] & 0x700) + data;
CH.KC[0] = (CH.FOCT[0] << 2) | FKEY_TAB[CH.FNUM[0] >> 7];
CH.SLOT[0].Finc = -1;
break;
case 0xA4:
if( (address&0x100)!=0 ) num += 3;
CH = YM2612_CHANNEL[num];
CH.FNUM[0] = (CH.FNUM[0] & 0x0FF) + ((int) (data & 0x07) << 8);
CH.FOCT[0] = (data & 0x38) >> 3;
CH.KC[0] = (CH.FOCT[0] << 2) | FKEY_TAB[CH.FNUM[0] >> 7];
CH.SLOT[0].Finc = -1;
break;
case 0xA8:
if(address<0x100){
num++;
YM2612_CHANNEL[2].FNUM[num] = (YM2612_CHANNEL[2].FNUM[num] & 0x700) + data;
YM2612_CHANNEL[2].KC[num] = (YM2612_CHANNEL[2].FOCT[num] << 2) | FKEY_TAB[YM2612_CHANNEL[2].FNUM[num] >> 7];
YM2612_CHANNEL[2].SLOT[0].Finc = -1;
}
break;
case 0xAC:
if(address < 0x100){
num++;
YM2612_CHANNEL[2].FNUM[num] = (YM2612_CHANNEL[2].FNUM[num] & 0x0FF) + ((int) (data & 0x07) << 8);
YM2612_CHANNEL[2].FOCT[num] = (data & 0x38) >> 3;
YM2612_CHANNEL[2].KC[num] = (YM2612_CHANNEL[2].FOCT[num] << 2) | FKEY_TAB[YM2612_CHANNEL[2].FNUM[num] >> 7];
YM2612_CHANNEL[2].SLOT[0].Finc = -1;
}
break;
case 0xB0:
if( (address&0x100)!=0 ) num += 3;
CH = YM2612_CHANNEL[num];
if(CH.ALGO != (data&7)){
CH.ALGO = data & 7;
CH.SLOT[0].ChgEnM = 0;
CH.SLOT[1].ChgEnM = 0;
CH.SLOT[2].ChgEnM = 0;
CH.SLOT[3].ChgEnM = 0;
}
CH.FB = 9 - ((data >> 3) & 7); // Real thing ?
// if(CH.FB = ((data >> 3) & 7)) CH.FB = 9 - CH.FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound...
// else CH.FB = 31;
break;
case 0xB4:
if( (address&0x100)!=0 ) num += 3;
CH = YM2612_CHANNEL[num];
if( (data&0x80)!=0 ) CH.LEFT = 0xFFFFFFFF;
else CH.LEFT = 0;
if( (data&0x40)!=0 ) CH.RIGHT = 0xFFFFFFFF;
else CH.RIGHT = 0;
CH.AMS = LFO_AMS_TAB[(data >> 4) & 3];
CH.FMS = LFO_FMS_TAB[data & 7];
if(CH.SLOT[0].AMSon!=0) CH.SLOT[0].AMS = CH.AMS;
else CH.SLOT[0].AMS = 31;
if(CH.SLOT[1].AMSon!=0) CH.SLOT[1].AMS = CH.AMS;
else CH.SLOT[1].AMS = 31;
if(CH.SLOT[2].AMSon!=0) CH.SLOT[2].AMS = CH.AMS;
else CH.SLOT[2].AMS = 31;
if(CH.SLOT[3].AMSon!=0) CH.SLOT[3].AMS = CH.AMS;
else CH.SLOT[3].AMS = 31;
break;
}
return 0;
}
private final int setYM(int address, int data){ // INT, UCHAR
cChannel CH;
int nch;
switch(address){
case 0x22:
if( (data&8)!=0 ){
// Cool Spot music 1, LFO modified severals time which
// distorts the sound, have to check that on a real genesis...
YM2612_LFOinc = LFO_INC_TAB[data & 7];
} else {
YM2612_LFOinc = YM2612_LFOcnt = 0;
}
break;
case 0x24:
YM2612_TimerA = (YM2612_TimerA & 0x003) | (((int) data) << 2);
if(YM2612_TimerAL != ((1024 - YM2612_TimerA) << 12)){
YM2612_TimerAcnt = YM2612_TimerAL = (1024 - YM2612_TimerA) << 12;
}
// System.out.println("Timer AH: " + Integer.toHexString(YM2612_TimerA));
break;
case 0x25:
YM2612_TimerA = (YM2612_TimerA & 0x3fc) | (data & 3);
if(YM2612_TimerAL != ((1024 - YM2612_TimerA) << 12)){
YM2612_TimerAcnt = YM2612_TimerAL = (1024 - YM2612_TimerA) << 12;
}
// System.out.println("Timer AL: " + Integer.toHexString(YM2612_TimerA));
break;
case 0x26:
YM2612_TimerB = data;
if(YM2612_TimerBL != ((256 - YM2612_TimerB) << (4 + 12))){
YM2612_TimerBcnt = YM2612_TimerBL = (256 - YM2612_TimerB) << (4 + 12);
}
// System.out.println("Timer B : " + Integer.toHexString(YM2612_TimerB));
break;
case 0x27:
if( ((data^YM2612_Mode)&0x40)!=0 ){
// We changed the channel 2 mode, so recalculate phase step
// This fix the punch sound in Street of Rage 2
YM2612_CHANNEL[2].SLOT[0].Finc = -1; // recalculate phase step
}
YM2612_Status &= (~data >> 4) & (data >> 2); // Reset Status
YM2612_Mode = data;
break;
case 0x28:
if((nch = data & 3) == 3) return 1;
if( (data&4)!=0 ) nch += 3;
CH = YM2612_CHANNEL[nch];
if( (data&0x10)!=0 ) KEY_ON(CH, S0);
else KEY_OFF(CH, S0);
if( (data&0x20)!=0 ) KEY_ON(CH, S1);
else KEY_OFF(CH, S1);
if( (data&0x40)!=0 ) KEY_ON(CH, S2);
else KEY_OFF(CH, S2);
if( (data&0x80)!=0 ) KEY_ON(CH, S3);
else KEY_OFF(CH, S3);
break;
case 0x2A:
break;
case 0x2B:
YM2612_DAC = data & 0x80;
break;
}
return 0;
}
/***********************************************
*
* Generation Methods
*
***********************************************/
private final void Env_NULL_Next(cSlot SL){
}
private final void Env_Attack_Next(cSlot SL){
SL.Ecnt = ENV_DECAY;
SL.Einc = SL.EincD;
SL.Ecmp = SL.SLL;
SL.Ecurp = DECAY;
}
private final void Env_Decay_Next(cSlot SL){
SL.Ecnt = SL.SLL;
SL.Einc = SL.EincS;
SL.Ecmp = ENV_END;
SL.Ecurp = SUSTAIN;
}
private final void Env_Sustain_Next(cSlot SL){
if(EnableSSGEG){
if( (SL.SEG&8)!=0 ){
if( (SL.SEG&1)!=0 ){
SL.Ecnt = ENV_END;
SL.Einc = 0;
SL.Ecmp = ENV_END + 1;
} else {
SL.Ecnt = 0;
SL.Einc = SL.EincA;
SL.Ecmp = ENV_DECAY;
SL.Ecurp = ATTACK;
}
SL.SEG ^= (SL.SEG & 2) << 1;
} else {
SL.Ecnt = ENV_END;
SL.Einc = 0;
SL.Ecmp = ENV_END + 1;
}
} else {
SL.Ecnt = ENV_END;
SL.Einc = 0;
SL.Ecmp = ENV_END + 1;
}
}
private final void Env_Release_Next(cSlot SL){
SL.Ecnt = ENV_END;
SL.Einc = 0;
SL.Ecmp = ENV_END + 1;
}
private final void ENV_NEXT_EVENT(int which, cSlot SL){
switch( which ){
case 0: Env_Attack_Next(SL); return;
case 1: Env_Decay_Next(SL); return;
case 2: Env_Sustain_Next(SL); return;
case 3: Env_Release_Next(SL); return;
//default: Env_NULL_Next(SL); return;
}
}
private final void calcChannel(int ALGO, cChannel CH){
// DO_FEEDBACK
in0 += (CH.S0_OUT[0] + CH.S0_OUT[1]) >> CH.FB;
CH.S0_OUT[1] = CH.S0_OUT[0];
CH.S0_OUT[0] = TL_TAB [SIN_TAB[(in0 >> SIN_LBITS) & SIN_MSK] + en0];
switch( ALGO ){
case 0:
in1 += CH.S0_OUT[1];
in2 += TL_TAB [SIN_TAB[(in1 >> SIN_LBITS) & SIN_MSK] + en1];
in3 += TL_TAB [SIN_TAB[(in2 >> SIN_LBITS) & SIN_MSK] + en2];
CH.OUTd = TL_TAB [SIN_TAB[(in3 >> SIN_LBITS) & SIN_MSK] + en3] >> MAIN_SHIFT;
break;
case 1:
in2 += CH.S0_OUT[1] + TL_TAB [SIN_TAB[(in1 >> SIN_LBITS) & SIN_MSK] + en1];
in3 += TL_TAB [SIN_TAB[(in2 >> SIN_LBITS) & SIN_MSK] + en2];
CH.OUTd = TL_TAB [SIN_TAB[(in3 >> SIN_LBITS) & SIN_MSK] + en3] >> MAIN_SHIFT;
break;
case 2:
in2 += TL_TAB [SIN_TAB[(in1 >> SIN_LBITS) & SIN_MSK] + en1];
in3 += CH.S0_OUT[1] + TL_TAB [SIN_TAB[(in2 >> SIN_LBITS) & SIN_MSK] + en2];
CH.OUTd = TL_TAB [SIN_TAB[(in3 >> SIN_LBITS) & SIN_MSK] + en3] >> MAIN_SHIFT;
break;
case 3:
in1 += CH.S0_OUT[1];
in3 += TL_TAB [SIN_TAB[(in1 >> SIN_LBITS) & SIN_MSK] + en1] + TL_TAB [SIN_TAB[(in2 >> SIN_LBITS) & SIN_MSK] + en2];
CH.OUTd = TL_TAB [SIN_TAB[(in3 >> SIN_LBITS) & SIN_MSK] + en3] >> MAIN_SHIFT;
break;
case 4:
in1 += CH.S0_OUT[1];
in3 += TL_TAB [SIN_TAB[(in2 >> SIN_LBITS) & SIN_MSK] + en2];
CH.OUTd = (TL_TAB [SIN_TAB[(in3 >> SIN_LBITS) & SIN_MSK] + en3] +
TL_TAB [SIN_TAB[(in1 >> SIN_LBITS) & SIN_MSK] + en1]) >> MAIN_SHIFT;
break;
case 5:
in1 += CH.S0_OUT[1];
in2 += CH.S0_OUT[1];
in3 += CH.S0_OUT[1];
CH.OUTd = (TL_TAB [SIN_TAB[(in3 >> SIN_LBITS) & SIN_MSK] + en3] +
TL_TAB [SIN_TAB[(in1 >> SIN_LBITS) & SIN_MSK] + en1] +
TL_TAB [SIN_TAB[(in2 >> SIN_LBITS) & SIN_MSK] + en2]) >> MAIN_SHIFT;
break;
case 6:
in1 += CH.S0_OUT[1];
CH.OUTd = (TL_TAB [SIN_TAB[(in3 >> SIN_LBITS) & SIN_MSK] + en3] +
TL_TAB [SIN_TAB[(in1 >> SIN_LBITS) & SIN_MSK] + en1] +
TL_TAB [SIN_TAB[(in2 >> SIN_LBITS) & SIN_MSK] + en2]) >> MAIN_SHIFT;
break;
case 7:
CH.OUTd = (TL_TAB [SIN_TAB[(in3 >> SIN_LBITS) & SIN_MSK] + en3] +
TL_TAB [SIN_TAB[(in1 >> SIN_LBITS) & SIN_MSK] + en1] +
TL_TAB [SIN_TAB[(in2 >> SIN_LBITS) & SIN_MSK] + en2] +
CH.S0_OUT[1]) >> MAIN_SHIFT;
break;
}
// DO_LIMIT
if(CH.OUTd > LIMIT_CH_OUT) CH.OUTd = LIMIT_CH_OUT;
else if(CH.OUTd < -LIMIT_CH_OUT) CH.OUTd = -LIMIT_CH_OUT;
}
private final void processChannel(cChannel CH, int[] buf_lr, int OFFSET, int END, int ALGO){
if( ALGO<4 ){
if(CH.SLOT[S3].Ecnt == ENV_END)
return;
} else if( ALGO==4 ){
if((CH.SLOT[S1].Ecnt == ENV_END) && (CH.SLOT[S3].Ecnt == ENV_END))
return;
} else if( ALGO<7 ){
if((CH.SLOT[S1].Ecnt == ENV_END) && (CH.SLOT[S2].Ecnt == ENV_END) && (CH.SLOT[S3].Ecnt == ENV_END))
return;
} else {
if((CH.SLOT[S0].Ecnt==ENV_END)&&(CH.SLOT[S1].Ecnt==ENV_END)&&
(CH.SLOT[S2].Ecnt==ENV_END)&&(CH.SLOT[S3].Ecnt==ENV_END))
return;
}
do
{
// GET_CURRENT_PHASE
in0 = CH.SLOT[S0].Fcnt;
in1 = CH.SLOT[S1].Fcnt;
in2 = CH.SLOT[S2].Fcnt;
in3 = CH.SLOT[S3].Fcnt;
// UPDATE_PHASE
CH.SLOT[S0].Fcnt += CH.SLOT[S0].Finc;
CH.SLOT[S1].Fcnt += CH.SLOT[S1].Finc;
CH.SLOT[S2].Fcnt += CH.SLOT[S2].Finc;
CH.SLOT[S3].Fcnt += CH.SLOT[S3].Finc;
// GET_CURRENT_ENV
if( (CH.SLOT[S0].SEG&4)!=0 ){
if( (en0 = ENV_TAB[(CH.SLOT[S0].Ecnt >> ENV_LBITS)] + CH.SLOT[S0].TLL) > ENV_MSK) en0 = 0;
else en0 ^= ENV_MSK;
}
else en0 = ENV_TAB[(CH.SLOT[S0].Ecnt >> ENV_LBITS)] + CH.SLOT[S0].TLL;
if( (CH.SLOT[S1].SEG&4)!=0 ){
if((en1 = ENV_TAB[(CH.SLOT[S1].Ecnt >> ENV_LBITS)] + CH.SLOT[S1].TLL) > ENV_MSK) en1 = 0;
else en1 ^= ENV_MSK;
}
else en1 = ENV_TAB[(CH.SLOT[S1].Ecnt >> ENV_LBITS)] + CH.SLOT[S1].TLL;
if( (CH.SLOT[S2].SEG & 4)!=0 ){
if((en2 = ENV_TAB[(CH.SLOT[S2].Ecnt >> ENV_LBITS)] + CH.SLOT[S2].TLL) > ENV_MSK) en2 = 0;
else en2 ^= ENV_MSK;
}
else en2 = ENV_TAB[(CH.SLOT[S2].Ecnt >> ENV_LBITS)] + CH.SLOT[S2].TLL;
if( (CH.SLOT[S3].SEG & 4)!=0 ){
if((en3 = ENV_TAB[(CH.SLOT[S3].Ecnt >> ENV_LBITS)] + CH.SLOT[S3].TLL) > ENV_MSK) en3 = 0;
else en3 ^= ENV_MSK;
}
else en3 = ENV_TAB[(CH.SLOT[S3].Ecnt >> ENV_LBITS)] + CH.SLOT[S3].TLL;
// UPDATE_ENV
if((CH.SLOT[S0].Ecnt += CH.SLOT[S0].Einc) >= CH.SLOT[S0].Ecmp){
ENV_NEXT_EVENT(CH.SLOT[S0].Ecurp, CH.SLOT[S0]);
}
if((CH.SLOT[S1].Ecnt += CH.SLOT[S1].Einc) >= CH.SLOT[S1].Ecmp){
ENV_NEXT_EVENT(CH.SLOT[S1].Ecurp, CH.SLOT[S1]);
}
if((CH.SLOT[S2].Ecnt += CH.SLOT[S2].Einc) >= CH.SLOT[S2].Ecmp){
ENV_NEXT_EVENT(CH.SLOT[S2].Ecurp, CH.SLOT[S2]);
}
if((CH.SLOT[S3].Ecnt += CH.SLOT[S3].Einc) >= CH.SLOT[S3].Ecmp){
ENV_NEXT_EVENT(CH.SLOT[S3].Ecurp, CH.SLOT[S3]);
}
calcChannel(ALGO, CH);
//DO_OUTPUT
buf_lr[OFFSET] += (CH.OUTd & CH.LEFT);
buf_lr[OFFSET+1] += (CH.OUTd & CH.RIGHT);
OFFSET += 2;
}
while ( OFFSET < END );
}
private final void processChannel_LFO(cChannel CH, int[] buf_lr, int OFFSET, int END, int ALGO){
if( ALGO<4 ){
if(CH.SLOT[S3].Ecnt == ENV_END)
return;
} else if( ALGO==4 ){
if((CH.SLOT[S1].Ecnt == ENV_END) && (CH.SLOT[S3].Ecnt == ENV_END))
return;
} else if( ALGO<7 ){
if((CH.SLOT[S1].Ecnt == ENV_END) && (CH.SLOT[S2].Ecnt == ENV_END) && (CH.SLOT[S3].Ecnt == ENV_END))
return;
} else {
if((CH.SLOT[S0].Ecnt==ENV_END) && (CH.SLOT[S1].Ecnt==ENV_END) &&
(CH.SLOT[S2].Ecnt==ENV_END) && (CH.SLOT[S3].Ecnt==ENV_END))
return;
}
do
{
final int i = OFFSET >> 1;
// GET_CURRENT_PHASE
in0 = CH.SLOT[S0].Fcnt;
in1 = CH.SLOT[S1].Fcnt;
in2 = CH.SLOT[S2].Fcnt;
in3 = CH.SLOT[S3].Fcnt;
// UPDATE_PHASE_LFO
int freq_LFO=(CH.FMS * LFO_FREQ_UP[i]) >> (LFO_HBITS - 1);
if( freq_LFO != 0 ){
CH.SLOT[S0].Fcnt += CH.SLOT[S0].Finc + ((CH.SLOT[S0].Finc * freq_LFO) >> LFO_FMS_LBITS);
CH.SLOT[S1].Fcnt += CH.SLOT[S1].Finc + ((CH.SLOT[S1].Finc * freq_LFO) >> LFO_FMS_LBITS);
CH.SLOT[S2].Fcnt += CH.SLOT[S2].Finc + ((CH.SLOT[S2].Finc * freq_LFO) >> LFO_FMS_LBITS);
CH.SLOT[S3].Fcnt += CH.SLOT[S3].Finc + ((CH.SLOT[S3].Finc * freq_LFO) >> LFO_FMS_LBITS);
} else {
CH.SLOT[S0].Fcnt += CH.SLOT[S0].Finc;
CH.SLOT[S1].Fcnt += CH.SLOT[S1].Finc;
CH.SLOT[S2].Fcnt += CH.SLOT[S2].Finc;
CH.SLOT[S3].Fcnt += CH.SLOT[S3].Finc;
}
// GET_CURRENT_ENV_LFO
int env_LFO = LFO_ENV_UP[i];
if( (CH.SLOT[S0].SEG&4)!=0 ){
if((en0 = ENV_TAB[(CH.SLOT[S0].Ecnt >> ENV_LBITS)] + CH.SLOT[S0].TLL) > ENV_MSK) en0 = 0;
else en0 = (en0 ^ ENV_MSK) + (env_LFO >> CH.SLOT[S0].AMS);
}
else en0 = ENV_TAB[(CH.SLOT[S0].Ecnt >> ENV_LBITS)] + CH.SLOT[S0].TLL + (env_LFO >> CH.SLOT[S0].AMS);
if( (CH.SLOT[S1].SEG & 4)!=0 ){
if((en1 = ENV_TAB[(CH.SLOT[S1].Ecnt >> ENV_LBITS)] + CH.SLOT[S1].TLL) > ENV_MSK) en1 = 0;
else en1 = (en1 ^ ENV_MSK) + (env_LFO >> CH.SLOT[S1].AMS);
}
else en1 = ENV_TAB[(CH.SLOT[S1].Ecnt >> ENV_LBITS)] + CH.SLOT[S1].TLL + (env_LFO >> CH.SLOT[S1].AMS);
if( (CH.SLOT[S2].SEG & 4)!=0 ){
if((en2 = ENV_TAB[(CH.SLOT[S2].Ecnt >> ENV_LBITS)] + CH.SLOT[S2].TLL) > ENV_MSK) en2 = 0;
else en2 = (en2 ^ ENV_MSK) + (env_LFO >> CH.SLOT[S2].AMS);
}
else en2 = ENV_TAB[(CH.SLOT[S2].Ecnt >> ENV_LBITS)] + CH.SLOT[S2].TLL + (env_LFO >> CH.SLOT[S2].AMS);
if( (CH.SLOT[S3].SEG & 4)!=0 ){
if((en3 = ENV_TAB[(CH.SLOT[S3].Ecnt >> ENV_LBITS)] + CH.SLOT[S3].TLL) > ENV_MSK) en3 = 0;
else en3 = (en3 ^ ENV_MSK) + (env_LFO >> CH.SLOT[S3].AMS);
}
else
en3 = ENV_TAB[(CH.SLOT[S3].Ecnt >> ENV_LBITS)] + CH.SLOT[S3].TLL + (env_LFO >> CH.SLOT[S3].AMS);
// UPDATE_ENV
if((CH.SLOT[S0].Ecnt += CH.SLOT[S0].Einc) >= CH.SLOT[S0].Ecmp)
ENV_NEXT_EVENT(CH.SLOT[S0].Ecurp, CH.SLOT[S0]);
if((CH.SLOT[S1].Ecnt += CH.SLOT[S1].Einc) >= CH.SLOT[S1].Ecmp)
ENV_NEXT_EVENT(CH.SLOT[S1].Ecurp, CH.SLOT[S1]);
if((CH.SLOT[S2].Ecnt += CH.SLOT[S2].Einc) >= CH.SLOT[S2].Ecmp)
ENV_NEXT_EVENT(CH.SLOT[S2].Ecurp, CH.SLOT[S2]);
if((CH.SLOT[S3].Ecnt += CH.SLOT[S3].Einc) >= CH.SLOT[S3].Ecmp)
ENV_NEXT_EVENT(CH.SLOT[S3].Ecurp, CH.SLOT[S3]);
calcChannel(ALGO, CH);
//DO_OUTPUT
int left = (CH.OUTd & CH.LEFT);
int right = (CH.OUTd & CH.RIGHT);
buf_lr[OFFSET] += left;
buf_lr[OFFSET+1] += right;
OFFSET += 2;
}
while ( OFFSET < END );
}
private final void updateChannel(int ALGO, cChannel CH, int[] buf_lr, int OFFSET, int END){
if( ALGO < 8 ){
processChannel(CH, buf_lr, OFFSET, END, ALGO);
} else {
processChannel_LFO(CH, buf_lr, OFFSET, END, ALGO-8);
}
}
}