aica: use log scale for attack phase

Change AEG starting envelope value from 3ff to 280
Use log scale instead of linear during attack phase
This commit is contained in:
Flyinghead 2019-09-10 16:21:51 +02:00
parent 0bd95cbba8
commit 08d7cc68fd
1 changed files with 93 additions and 78 deletions

View File

@ -52,26 +52,43 @@ static const u32 SendLevel[16] =
s32 tl_lut[256 + 768]; //xx.15 format. >=255 is muted
//in ms :)
static const double AEG_Attack_Time[]=
static const double AEG_Attack_Time[64] =
{
-1,-1,8100.0,6900.0,6000.0,4800.0,4000.0,3400.0,3000.0,2400.0,2000.0,1700.0,1500.0,
1200.0,1000.0,860.0,760.0,600.0,500.0,430.0,380.0,300.0,250.0,220.0,190.0,150.0,130.0,110.0,95.0,
76.0,63.0,55.0,47.0,38.0,31.0,27.0,24.0,19.0,15.0,13.0,12.0,9.4,7.9,6.8,6.0,4.7,3.8,3.4,3.0,2.4,
2.0,1.8,1.6,1.3,1.1,0.93,0.85,0.65,0.53,0.44,0.40,0.35,0.0,0.0
};
static const double AEG_DSR_Time[]=
{ -1,-1,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0,
static const double AEG_DSR_Time[64] =
{
-1,-1,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0,
14800.0,12700.0,11100.0,8900.0,7400.0,6300.0,5500.0,4400.0,3700.0,3200.0,2800.0,2200.0,1800.0,1600.0,1400.0,1100.0,
920.0,790.0,690.0,550.0,460.0,390.0,340.0,270.0,230.0,200.0,170.0,140.0,110.0,98.0,85.0,68.0,57.0,49.0,43.0,34.0,
28.0,25.0,22.0,18.0,14.0,12.0,11.0,8.5,7.1,6.1,5.4,4.3,3.6,3.1
};
// These times come from the documentation but don't sound correct.
// HT uses the AEG decay times instead and it sounds better.
static const double FEG_Time[] =
{
-1, -1, 472800.0, 405200.0, 354400.0, 283600.0, 236400.0, 202800.0,
177200.0, 142000.0, 118400.0, 101200.0, 88800.0, 70800.0, 59200.0, 50800.0,
44400.0, 35600.0, 29600.0, 25200.0, 22000.0, 17600.0, 14800.0, 12800.0,
11200.0, 8800.0, 7200.0, 6400.0, 5600.0, 4400.0, 3680.0, 3160.0,
2760.0, 2220.0, 1840.0, 1560.0, 1360.0, 1080.0, 920.0, 800.0,
680.0, 560.0, 440.0, 392.0, 340.0, 272.0, 228.0, 196.0,
172.0, 158.0, 136.0, 100.0, 88.0, 72.0, 56.0, 48.0,
44.0, 34.0, 28.0, 24.0, 22.0, 17.0, 14.0, 12.0
};
static const float PLFOS_Scale[8] = { 0.f, 3.61f, 7.22f, 14.44f, 28.88f, 57.75f, 115.5f, 231.f };
static int PLFO_Scales[8][256];
#define AEG_STEP_BITS (16)
#define EG_STEP_BITS (16)
#define AEG_ATTACK_SHIFT 16
//Steps per sample
u32 AEG_ATT_SPS[64];
u32 AEG_DSR_SPS[64];
u32 FEG_SPS[64];
static const char* stream_names[]=
{
@ -364,8 +381,8 @@ struct ChannelEx
struct
{
s32 val;
__forceinline s32 GetValue() { return val>>AEG_STEP_BITS;}
void SetValue(u32 aegb) { val=aegb<<AEG_STEP_BITS; }
__forceinline s32 GetValue() { return val >> EG_STEP_BITS;}
void SetValue(u32 aegb) { val = aegb << EG_STEP_BITS; }
_EG_state state=EG_Attack;
@ -379,8 +396,8 @@ struct ChannelEx
struct
{
u32 value;
__forceinline u32 GetValue() { return value >> AEG_STEP_BITS;}
void SetValue(u32 fegb) { value = fegb << AEG_STEP_BITS; }
__forceinline u32 GetValue() { return value >> EG_STEP_BITS;}
void SetValue(u32 fegb) { value = fegb << EG_STEP_BITS; }
_EG_state state = EG_Attack;
@ -554,7 +571,7 @@ struct ChannelEx
// reset AEG
SetAegState(EG_Attack);
AEG.SetValue(0x3FF);//start from 0x3FF ? .. it seems so !
AEG.SetValue(0x280); // start value taken from HT
//reset FEG
SetFegState(EG_Attack);
@ -621,19 +638,20 @@ struct ChannelEx
loop.LEA=ccd->LEA;
}
u32 EG_BaseRate()
s32 EG_BaseRate()
{
s32 rv=ccd->KRS+(ccd->FNS>>9);
if (ccd->KRS==0xF)
rv-=0xF;
if (ccd->OCT&8)
rv-=(16-ccd->OCT)*2;
else
rv+=ccd->OCT*2;
return rv;
s32 effrate = 0;
if (ccd->KRS < 0xF)
{
effrate += (ccd->FNS >> 9) & 1;
effrate += ccd->KRS * 2;
effrate += (ccd->OCT ^ 8) - 8;
}
return effrate;
}
u32 EG_EffRate(u32 base_rate, u32 rate)
u32 EG_EffRate(s32 base_rate, u32 rate)
{
s32 rv = base_rate + rate * 2;
@ -740,10 +758,10 @@ struct ChannelEx
ccd->FAR, ccd->FD1R, ccd->FD2R, ccd->FRR);
FEG.q = qtable[ccd->Q];
s32 base_rate = EG_BaseRate();
FEG.AttackRate = AEG_DSR_SPS[EG_EffRate(base_rate, ccd->FAR)];
FEG.Decay1Rate = AEG_DSR_SPS[EG_EffRate(base_rate, ccd->FD1R)];
FEG.Decay2Rate = AEG_DSR_SPS[EG_EffRate(base_rate, ccd->FD2R)];
FEG.ReleaseRate = AEG_DSR_SPS[EG_EffRate(base_rate, ccd->FRR)];
FEG.AttackRate = FEG_SPS[EG_EffRate(base_rate, ccd->FAR)];
FEG.Decay1Rate = FEG_SPS[EG_EffRate(base_rate, ccd->FD1R)];
FEG.Decay2Rate = FEG_SPS[EG_EffRate(base_rate, ccd->FD2R)];
FEG.ReleaseRate = FEG_SPS[EG_EffRate(base_rate, ccd->FRR)];
}
//WHEE :D!
@ -842,13 +860,13 @@ struct ChannelEx
}
};
__forceinline SampleType DecodeADPCM(u32 sample,s32 prev,s32& quant)
static __forceinline SampleType DecodeADPCM(u32 sample,s32 prev,s32& quant)
{
s32 sign=1-2*(sample/8);
u32 data=sample&7;
/*(1 - 2 * L4) * (L3 + L2/2 +L1/4 + 1/8) * quantized width (ƒΆn) + decode value (Xn - 1) */
/*(1 - 2 * L4) * (L3 + L2/2 +L1/4 + 1/8) * quantized width (Dn) + decode value (Xn - 1) */
SampleType rv = (quant * adpcm_scale[data]) >> 3;
if (rv > 0x7FFF)
rv = 0x7FFF;
@ -993,7 +1011,7 @@ void StreamStep(ChannelEx* ch)
if (LPCTL == 0)
ch->disable();
else
step_printf("[%d]LPCTL : Looping LSA %x LEA %x", ch->ChannelNumber, ch->loop.LSA, ch->loop.LEA);
key_printf("[%d]LPCTL : Looping LSA %x LEA %x", ch->ChannelNumber, ch->loop.LSA, ch->loop.LEA);
}
ch->CA=CA;
@ -1066,14 +1084,14 @@ void AegStep(ChannelEx* ch)
switch(state)
{
case EG_Attack:
if (ch->AEG.AttackRate != 0)
{
//wii
ch->AEG.val-=ch->AEG.AttackRate;
if (ch->AEG.GetValue()<=0)
ch->AEG.val -= (((u64)ch->AEG.val << AEG_ATTACK_SHIFT) / ch->AEG.AttackRate) + 1;
if (ch->AEG.GetValue() <= 0)
{
if (!ch->ccd->LPSLNK)
{
aeg_printf("[%d]AEG_step : Switching to EG_Decay1 %d", ch->ChannelNumber, ch->AEG.GetValue());
aeg_printf("[%d]AEG_step : Switching to EG_Decay1", ch->ChannelNumber);
ch->SetAegState(EG_Decay1);
}
ch->AEG.SetValue(0);
@ -1081,38 +1099,29 @@ void AegStep(ChannelEx* ch)
}
break;
case EG_Decay1:
ch->AEG.val += ch->AEG.Decay1Rate;
if (((u32)ch->AEG.GetValue()) >= ch->AEG.Decay2Value)
{
//x2
ch->AEG.val+=ch->AEG.Decay1Rate;
if (((u32)ch->AEG.GetValue())>=ch->AEG.Decay2Value)
{
aeg_printf("[%d]AEG_step : Switching to EG_Decay2 @ %x", ch->ChannelNumber, ch->AEG.GetValue());
ch->SetAegState(EG_Decay2);
}
aeg_printf("[%d]AEG_step : Switching to EG_Decay2", ch->ChannelNumber);
ch->SetAegState(EG_Decay2);
}
break;
case EG_Decay2:
ch->AEG.val += ch->AEG.Decay2Rate;
if (ch->AEG.GetValue() >= 0x3FF)
{
//x3
ch->AEG.val+=ch->AEG.Decay2Rate;
if (ch->AEG.GetValue()>=0x3FF)
{
aeg_printf("[%d]AEG_step : Switching to EG_Release @ %x", ch->ChannelNumber, ch->AEG.GetValue());
ch->AEG.SetValue(0x3FF);
ch->SetAegState(EG_Release);
}
aeg_printf("[%d]AEG_step : Switching to EG_Release", ch->ChannelNumber);
ch->AEG.SetValue(0x3FF);
ch->SetAegState(EG_Release);
}
break;
case EG_Release: //only on key_off ?
case EG_Release: //only on key_off
ch->AEG.val += ch->AEG.ReleaseRate;
if (ch->AEG.GetValue() >= 0x3FF)
{
ch->AEG.val+=ch->AEG.ReleaseRate;
if (ch->AEG.GetValue()>=0x3FF)
{
aeg_printf("[%d]AEG_step : EG_Release End @ %x", ch->ChannelNumber, ch->AEG.GetValue());
ch->AEG.SetValue(0x3FF); // TODO: mnn, should we do anything about it running wild ?
ch->disable(); // TODO: Is this ok here? It's a speed optimisation (since the channel is muted)
}
aeg_printf("[%d]AEG_step : EG_Release End @ %x", ch->ChannelNumber, ch->AEG.GetValue());
ch->AEG.SetValue(0x3FF); // TODO: mnn, should we do anything about it running wild ?
ch->disable(); // TODO: Is this ok here? It's a speed optimisation (since the channel is muted)
}
break;
}
@ -1143,7 +1152,7 @@ void FegStep(ChannelEx* ch)
target = ch->ccd->FLV4;
break;
}
target <<= AEG_STEP_BITS;
target <<= EG_STEP_BITS;
if (ch->FEG.value < target)
{
u32 maxd = target - ch->FEG.value;
@ -1162,13 +1171,13 @@ void FegStep(ChannelEx* ch)
{
if (ch->FEG.state < EG_Decay2)
{
feg_printf("[%d]FEG_step : Switching to next state: %d Freq %x", ch->ChannelNumber, (int)ch->FEG.state + 1, target >> AEG_STEP_BITS);
feg_printf("[%d]FEG_step : Switching to next state: %d Freq %x", ch->ChannelNumber, (int)ch->FEG.state + 1, target >> EG_STEP_BITS);
ch->SetFegState((_EG_state)((int)ch->FEG.state + 1));
}
}
}
void staticinitialise()
static void staticinitialise()
{
STREAM_STEP_LUT[0][0][0]=&StreamStep<0,0,0>;
STREAM_STEP_LUT[1][0][0]=&StreamStep<1,0,0>;
@ -1220,30 +1229,39 @@ void staticinitialise()
PLFOWS_CALC[2]=&CalcPlfo<2>;
PLFOWS_CALC[3]=&CalcPlfo<3>;
}
#define AicaChannel ChannelEx
ChannelEx ChannelEx::Chans[64];
AicaChannel AicaChannel::Chans[64];
#define Chans ChannelEx::Chans
#define Chans AicaChannel::Chans
double dbToval(double db)
static u32 CalcEgSteps(float t)
{
return pow(10,db/20.0);
}
u32 CalcAegSteps(float t)
{
const double aeg_allsteps=1024*(1<<AEG_STEP_BITS)-1;
const double eg_allsteps = 1024 * (1 << EG_STEP_BITS) - 1;
if (t<0)
if (t < 0)
return 0;
if (t==0)
return (u32)aeg_allsteps;
if (t == 0)
return (u32)eg_allsteps;
//44.1*ms = samples
double scnt=44.1*t;
double steps=aeg_allsteps/scnt;
double scnt = 44.1 * t;
double steps = eg_allsteps / scnt;
return (u32)lround(steps);
}
static u32 CalcAttackEgSteps(float t)
{
if (t < 0)
return 0;
if (t == 0)
return 1 << AEG_ATTACK_SHIFT;
//44.1*ms = samples
double scnt = 44.1 * t;
double factor = (1.0 / (1.0 - 1.0 / pow(0x280, 1.0 / scnt))) * (1 << AEG_ATTACK_SHIFT);
return (u32)lround(factor);
}
void sgc_Init()
{
staticinitialise();
@ -1266,8 +1284,10 @@ void sgc_Init()
for (int i=0;i<64;i++)
{
AEG_ATT_SPS[i]=CalcAegSteps(AEG_Attack_Time[i]);
AEG_DSR_SPS[i]=CalcAegSteps(AEG_DSR_Time[i]);
AEG_ATT_SPS[i] = CalcAttackEgSteps(AEG_Attack_Time[i]);
AEG_DSR_SPS[i]=CalcEgSteps(AEG_DSR_Time[i]);
FEG_SPS[i] = CalcEgSteps(AEG_DSR_Time[i]);
//FEG_SPS[i] = CalcEgSteps(FEG_Time[i]);
}
for (int i=0;i<64;i++)
Chans[i].Init(i,aica_reg);
@ -1345,8 +1365,6 @@ void WriteCommonReg8(u32 reg,u32 data)
s16 cdda_sector[CDDA_SIZE]={0};
u32 cdda_index=CDDA_SIZE<<1;
u32 samples_gen;
//no DSP for now in this version
void AICA_Sample32()
{
@ -1381,9 +1399,6 @@ void AICA_Sample32()
mxlr[i*2+1] += oRight;
}
}
#ifdef _WIN32
samples_gen += sg;
#endif
//OK , generated all Channels , now DSP/ect + final mix ;p
//CDDA EXTS input