aica: implement pitch LFO and LPF envelope
simplify channel serialization optimize aica channel reg write callbacks
This commit is contained in:
parent
afc8398628
commit
fbdbd2491d
|
@ -41,17 +41,8 @@ void WriteReg(u32 addr,u32 data)
|
|||
//Channel data
|
||||
u32 chan = addr >> 7;
|
||||
u32 reg = addr & 0x7F;
|
||||
if (sz==1)
|
||||
{
|
||||
WriteMemArr(aica_reg,addr,data,1);
|
||||
WriteChannelReg8(chan,reg);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMemArr(aica_reg,addr,data,2);
|
||||
WriteChannelReg8(chan,reg);
|
||||
WriteChannelReg8(chan,reg+1);
|
||||
}
|
||||
WriteMemArr(aica_reg, addr, data, sz);
|
||||
WriteChannelReg(chan, reg, sz);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
/*
|
||||
This file is part of reicast.
|
||||
|
||||
reicast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
reicast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Some PLFO and FEG code from Highly_Theoretical lib by Neill Corlett
|
||||
(https://github.com/kode54/Highly_Theoretical)
|
||||
*/
|
||||
|
||||
#include "sgc_if.h"
|
||||
#include "dsp.h"
|
||||
#include "aica_mem.h"
|
||||
|
@ -10,6 +30,7 @@ using namespace std;
|
|||
//#define CLIP_WARN
|
||||
#define key_printf(...) DEBUG_LOG(AICA, __VA_ARGS__)
|
||||
#define aeg_printf(...) DEBUG_LOG(AICA, __VA_ARGS__)
|
||||
#define feg_printf(...) DEBUG_LOG(AICA, __VA_ARGS__)
|
||||
#define step_printf(...) DEBUG_LOG(AICA, __VA_ARGS__)
|
||||
|
||||
#ifdef CLIP_WARN
|
||||
|
@ -23,30 +44,36 @@ using namespace std;
|
|||
s32 volume_lut[16];
|
||||
//255 -> mute
|
||||
//Converts Send levels to TL-compatible values (DISDL, etc)
|
||||
u32 SendLevel[16]={255,14<<3,13<<3,12<<3,11<<3,10<<3,9<<3,8<<3,7<<3,6<<3,5<<3,4<<3,3<<3,2<<3,1<<3,0<<3};
|
||||
static const u32 SendLevel[16] =
|
||||
{
|
||||
255, 14 << 3, 13 << 3, 12 << 3, 11 << 3, 10 << 3, 9 << 3, 8 << 3,
|
||||
7 << 3, 6 << 3, 5 << 3, 4 << 3, 3 << 3, 2 << 3, 1 << 3, 0 << 3
|
||||
};
|
||||
s32 tl_lut[256 + 768]; //xx.15 format. >=255 is muted
|
||||
|
||||
//in ms :)
|
||||
double AEG_Attack_Time[]=
|
||||
static const double AEG_Attack_Time[]=
|
||||
{
|
||||
-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
|
||||
};
|
||||
double AEG_DSR_Time[]=
|
||||
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,
|
||||
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
|
||||
};
|
||||
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)
|
||||
//Steps per sample
|
||||
u32 AEG_ATT_SPS[64];
|
||||
u32 AEG_DSR_SPS[64];
|
||||
|
||||
const char* stream_names[]=
|
||||
static const char* stream_names[]=
|
||||
{
|
||||
"0: 16-bit PCM (two's complement format)",
|
||||
"1: 8-bit PCM (two's complement format)",
|
||||
|
@ -55,17 +82,28 @@ const char* stream_names[]=
|
|||
};
|
||||
|
||||
//x.8 format
|
||||
const s32 adpcm_qs[8] =
|
||||
static const s32 adpcm_qs[8] =
|
||||
{
|
||||
0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266,
|
||||
};
|
||||
//x.3 format
|
||||
const s32 adpcm_scale[16] =
|
||||
static const s32 adpcm_scale[16] =
|
||||
{
|
||||
1,3,5,7,9,11,13,15,
|
||||
-1,-3,-5,-7,-9,-11,-13,-15,
|
||||
};
|
||||
|
||||
static const s32 qtable[32] = {
|
||||
0x0E00,0x0E80,0x0F00,0x0F80,
|
||||
0x1000,0x1080,0x1100,0x1180,
|
||||
0x1200,0x1280,0x1300,0x1380,
|
||||
0x1400,0x1480,0x1500,0x1580,
|
||||
0x1600,0x1680,0x1700,0x1780,
|
||||
0x1800,0x1880,0x1900,0x1980,
|
||||
0x1A00,0x1A80,0x1B00,0x1B80,
|
||||
0x1C00,0x1D00,0x1E00,0x1F00
|
||||
};
|
||||
|
||||
void AICA_Sample();
|
||||
|
||||
//Remove the fractional part , with rounding ;) -- does not need an extra bit
|
||||
|
@ -185,7 +223,9 @@ struct ChannelCommonData
|
|||
|
||||
//+28 TL[7:0] -- Q[4:0]
|
||||
u32 Q:5;
|
||||
u32 rez_28_0:3;
|
||||
u32 LPOFF:1; // confirmed but not documented: 0: LPF enabled, 1: LPF disabled
|
||||
u32 VOFF:1; // unconfirmed: 0: attenuation enabled, 1: attenuation disabled (TL, AEG, ALFO)
|
||||
u32 rez_28_0:1;
|
||||
|
||||
u32 TL:8;
|
||||
|
||||
|
@ -321,10 +361,6 @@ struct ChannelEx
|
|||
void (* StepStream)(ChannelEx* ch);
|
||||
void (* StepStreamInitial)(ChannelEx* ch);
|
||||
|
||||
u8 step_stream_lut1=0 ;
|
||||
u8 step_stream_lut2=0 ;
|
||||
u8 step_stream_lut3=0 ;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 val;
|
||||
|
@ -342,9 +378,21 @@ struct ChannelEx
|
|||
|
||||
struct
|
||||
{
|
||||
s32 value;
|
||||
u32 value;
|
||||
__forceinline u32 GetValue() { return value >> AEG_STEP_BITS;}
|
||||
void SetValue(u32 fegb) { value = fegb << AEG_STEP_BITS; }
|
||||
|
||||
_EG_state state = EG_Attack;
|
||||
} FEG;//i have to figure out how this works w/ AEG and channel state, and the iir values
|
||||
|
||||
SampleType prev1;
|
||||
SampleType prev2;
|
||||
s32 q;
|
||||
u32 AttackRate;
|
||||
u32 Decay1Rate;
|
||||
u32 Decay2Rate;
|
||||
u32 ReleaseRate;
|
||||
bool active = false;
|
||||
} FEG;
|
||||
|
||||
struct
|
||||
{
|
||||
|
@ -353,10 +401,8 @@ struct ChannelEx
|
|||
u8 state;
|
||||
u8 alfo;
|
||||
u8 alfo_shft;
|
||||
u8 plfo;
|
||||
u8 plfo_shft;
|
||||
u8 alfo_calc_lut=0 ;
|
||||
u8 plfo_calc_lut=0 ;
|
||||
fp_22_10 plfo_step;
|
||||
int *plfo_scale;
|
||||
void (* alfo_calc)(ChannelEx* ch);
|
||||
void (* plfo_calc)(ChannelEx* ch);
|
||||
__forceinline void Step(ChannelEx* ch) { counter--;if (counter==0) { state++; counter=start_value; alfo_calc(ch);plfo_calc(ch); } }
|
||||
|
@ -372,7 +418,7 @@ struct ChannelEx
|
|||
ccd=(ChannelCommonData*)&ccd_raw[cn*0x80];
|
||||
ChannelNumber = cn;
|
||||
for (u32 i=0;i<0x80;i++)
|
||||
RegWrite(i);
|
||||
RegWrite(i, 1);
|
||||
disable();
|
||||
}
|
||||
void disable()
|
||||
|
@ -405,6 +451,18 @@ struct ChannelEx
|
|||
{
|
||||
SampleType sample = InterpolateSample();
|
||||
|
||||
// Low-pass filter
|
||||
if (FEG.active)
|
||||
{
|
||||
u32 fv = FEG.GetValue();
|
||||
s32 f = (((fv & 0xFF) | 0x100) << 4) >> ((fv >> 8) ^ 0x1F);
|
||||
sample = f * sample + (0x2000 - f + FEG.q) * FEG.prev1 - FEG.q * FEG.prev2;
|
||||
sample >>= 13;
|
||||
clip16(sample);
|
||||
FEG.prev2 = FEG.prev1;
|
||||
FEG.prev1 = sample;
|
||||
}
|
||||
|
||||
//Volume & Mixer processing
|
||||
//All attenuations are added together then applied and mixed :)
|
||||
|
||||
|
@ -412,8 +470,16 @@ struct ChannelEx
|
|||
//*Att is up to 511
|
||||
//logtable handles up to 1024, anything >=255 is mute
|
||||
|
||||
u32 ofsatt=lfo.alfo+(AEG.GetValue()>>2);
|
||||
u32 ofsatt;
|
||||
if (ccd->VOFF == 1)
|
||||
{
|
||||
ofsatt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ofsatt = lfo.alfo + (AEG.GetValue() >> 2);
|
||||
ofsatt = min(ofsatt, (u32)255); // make sure it never gets more 255 -- it can happen with some alfo/aeg combinations
|
||||
}
|
||||
u32 const max_att = ((16 << 4) - 1) - ofsatt;
|
||||
|
||||
s32* logtable = ofsatt + tl_lut;
|
||||
|
@ -466,11 +532,19 @@ struct ChannelEx
|
|||
if (newstate==EG_Release)
|
||||
ccd->KYONB=0;
|
||||
}
|
||||
|
||||
void SetFegState(_EG_state newstate)
|
||||
{
|
||||
StepFEG = FEG_STEP_LUT[newstate];
|
||||
FEG.state = newstate;
|
||||
if (newstate == EG_Attack)
|
||||
{
|
||||
FEG.SetValue(ccd->FLV0);
|
||||
FEG.prev1 = 0;
|
||||
FEG.prev2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void KEY_ON()
|
||||
{
|
||||
if (AEG.state==EG_Release)
|
||||
|
@ -496,9 +570,11 @@ struct ChannelEx
|
|||
adpcm.Reset(this);
|
||||
|
||||
StepStreamInitial(this);
|
||||
|
||||
key_printf("[%d] KEY_ON %s @ %f Hz, loop %d, AEG AR %d DC1R %d DC2V %d DC2R %d RR %d", ChannelNumber, stream_names[ccd->PCMS], (44100.0 * update_rate) / 1024, ccd->LPCTL,
|
||||
AEG.AttackRate, AEG.Decay1Rate, AEG.Decay2Value, AEG.Decay2Rate, AEG.ReleaseRate);
|
||||
key_printf("[%d] KEY_ON %s @ %f Hz, loop %d - AEG AR %d DC1R %d DC2V %d DC2R %d RR %d - KRS %d OCT %d FNS %d - PFLOS %d PFLOWS %d",
|
||||
ChannelNumber, stream_names[ccd->PCMS], (44100.0 * update_rate) / 1024, ccd->LPCTL,
|
||||
ccd->AR, ccd->D1R, ccd->DL << 5, ccd->D2R, ccd->RR,
|
||||
ccd->KRS, ccd->OCT, ccd->FNS >> 9,
|
||||
ccd->PLFOS, ccd->PLFOWS);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -511,6 +587,7 @@ struct ChannelEx
|
|||
{
|
||||
key_printf("[%d] KEY_OFF -> Release", ChannelNumber);
|
||||
SetAegState(EG_Release);
|
||||
SetFegState(EG_Release);
|
||||
//switch to release state
|
||||
}
|
||||
else
|
||||
|
@ -527,9 +604,6 @@ struct ChannelEx
|
|||
|
||||
StepStream=STREAM_STEP_LUT[fmt][ccd->LPCTL][ccd->LPSLNK];
|
||||
StepStreamInitial=STREAM_INITAL_STEP_LUT[fmt];
|
||||
step_stream_lut1 = fmt ;
|
||||
step_stream_lut2 = ccd->LPCTL ;
|
||||
step_stream_lut3 = ccd->LPSLNK ;
|
||||
}
|
||||
//SA,PCMS
|
||||
void UpdateSA()
|
||||
|
@ -546,31 +620,37 @@ struct ChannelEx
|
|||
loop.LSA=ccd->LSA;
|
||||
loop.LEA=ccd->LEA;
|
||||
}
|
||||
//
|
||||
u32 AEG_EffRate(u32 re)
|
||||
|
||||
u32 EG_BaseRate()
|
||||
{
|
||||
s32 rv=ccd->KRS+(ccd->FNS>>9) + re*2;
|
||||
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;
|
||||
|
||||
if (rv<0)
|
||||
rv=0;
|
||||
if (rv>0x3f)
|
||||
rv=0x3f;
|
||||
return rv;
|
||||
}
|
||||
|
||||
u32 EG_EffRate(u32 base_rate, u32 rate)
|
||||
{
|
||||
s32 rv = base_rate + rate * 2;
|
||||
|
||||
clip(rv, 0, 0x3f);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//D2R,D1R,AR,DL,RR,KRS, [OCT,FNS] for now
|
||||
void UpdateAEG()
|
||||
{
|
||||
AEG.AttackRate = AEG_ATT_SPS[AEG_EffRate(ccd->AR)];
|
||||
AEG.Decay1Rate = AEG_DSR_SPS[AEG_EffRate(ccd->D1R)];
|
||||
s32 base_rate = EG_BaseRate();
|
||||
AEG.AttackRate = AEG_ATT_SPS[EG_EffRate(base_rate, ccd->AR)];
|
||||
AEG.Decay1Rate = AEG_DSR_SPS[EG_EffRate(base_rate, ccd->D1R)];
|
||||
AEG.Decay2Value = ccd->DL<<5;
|
||||
AEG.Decay2Rate = AEG_DSR_SPS[AEG_EffRate(ccd->D2R)];
|
||||
AEG.ReleaseRate = AEG_DSR_SPS[AEG_EffRate(ccd->RR)];
|
||||
AEG.Decay2Rate = AEG_DSR_SPS[EG_EffRate(base_rate, ccd->D2R)];
|
||||
AEG.ReleaseRate = AEG_DSR_SPS[EG_EffRate(base_rate, ccd->RR)];
|
||||
}
|
||||
//OCT,FNS
|
||||
void UpdatePitch()
|
||||
|
@ -586,7 +666,7 @@ struct ChannelEx
|
|||
this->update_rate=update_rate;
|
||||
}
|
||||
|
||||
//LFORE,LFOF,PLFOWS,PLFOS,LFOWS,ALFOS
|
||||
//LFORE,LFOF,PLFOWS,PLFOS,ALFOWS,ALFOS
|
||||
void UpdateLFO()
|
||||
{
|
||||
{
|
||||
|
@ -599,13 +679,11 @@ struct ChannelEx
|
|||
lfo.SetStartValue(O);
|
||||
}
|
||||
|
||||
lfo.plfo_shft=8-ccd->PLFOS;
|
||||
lfo.alfo_shft=8-ccd->ALFOS;
|
||||
|
||||
lfo.alfo_calc=ALFOWS_CALC[ccd->ALFOWS];
|
||||
lfo.plfo_calc=PLFOWS_CALC[ccd->PLFOWS];
|
||||
lfo.alfo_calc_lut=ccd->ALFOWS;
|
||||
lfo.plfo_calc_lut=ccd->PLFOWS;
|
||||
lfo.plfo_scale = PLFO_Scales[ccd->PLFOS];
|
||||
|
||||
if (ccd->LFORE)
|
||||
{
|
||||
|
@ -628,7 +706,8 @@ struct ChannelEx
|
|||
//TL,DISDL,DIPAN,IMXL
|
||||
void UpdateAtts()
|
||||
{
|
||||
u32 attFull=ccd->TL+SendLevel[ccd->DISDL];
|
||||
u32 total_level = ccd->VOFF ? 0 : ccd->TL;
|
||||
u32 attFull = total_level + SendLevel[ccd->DISDL];
|
||||
u32 attPan = attFull + SendLevel[(~ccd->DIPAN) & 0xF];
|
||||
|
||||
//0x1* -> R decreases
|
||||
|
@ -643,29 +722,40 @@ struct ChannelEx
|
|||
VolMix.DRAtt=attFull;
|
||||
}
|
||||
|
||||
VolMix.DSPAtt = ccd->TL+SendLevel[ccd->IMXL];
|
||||
VolMix.DSPAtt = total_level + SendLevel[ccd->IMXL];
|
||||
}
|
||||
|
||||
//Q,FLV0,FLV1,FLV2,FLV3,FLV4,FAR,FD1R,FD2R,FRR
|
||||
void UpdateFEG()
|
||||
{
|
||||
//this needs to be filled
|
||||
FEG.active = ccd->LPOFF == 0
|
||||
&& (ccd->FLV0 < 0x1ff7 || ccd->FLV1 < 0x1ff7
|
||||
|| ccd->FLV2 < 0x1ff7 || ccd->FLV3 < 0x1ff7
|
||||
|| ccd->FLV4 < 0x1ff7);
|
||||
if (!FEG.active)
|
||||
return;
|
||||
feg_printf("FEG active channel %d Q %d FLV: %05x %05x %05x %05x %05x AR %02x FD1R %02x FD2R %02x FRR %02x",
|
||||
ChannelNumber, ccd->Q,
|
||||
ccd->FLV0, ccd->FLV1, ccd->FLV2, ccd->FLV3, ccd->FLV4,
|
||||
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)];
|
||||
}
|
||||
|
||||
//WHEE :D!
|
||||
void RegWrite(u32 offset)
|
||||
void RegWrite(u32 offset, int size)
|
||||
{
|
||||
switch(offset)
|
||||
{
|
||||
case 0x00: //yay ?
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
UpdateStreamStep();
|
||||
UpdateSA();
|
||||
break;
|
||||
|
||||
case 0x01: //yay ?
|
||||
UpdateStreamStep();
|
||||
UpdateSA();
|
||||
if (ccd->KYONEX)
|
||||
if ((offset == 0x01 || size == 2) && ccd->KYONEX)
|
||||
{
|
||||
ccd->KYONEX=0;
|
||||
for (int i = 0; i < 64; i++)
|
||||
|
@ -708,7 +798,7 @@ struct ChannelEx
|
|||
break;
|
||||
|
||||
case 0x1C://ALFOS,ALFOWS,PLFOS
|
||||
case 0x1D://PLFOWS,LFOF,RE
|
||||
case 0x1D://PLFOWS,LFOF,LFORE
|
||||
UpdateLFO();
|
||||
break;
|
||||
|
||||
|
@ -724,10 +814,10 @@ struct ChannelEx
|
|||
break;
|
||||
|
||||
case 0x28://Q
|
||||
UpdateFEG();
|
||||
break;
|
||||
|
||||
case 0x29://TL
|
||||
if (size == 2 || offset == 0x28)
|
||||
UpdateFEG();
|
||||
if (size == 2 || offset == 0x29)
|
||||
UpdateAtts();
|
||||
break;
|
||||
|
||||
|
@ -872,7 +962,7 @@ void StepDecodeSampleInitial(ChannelEx* ch)
|
|||
template<s32 PCMS,u32 LPCTL,u32 LPSLNK>
|
||||
void StreamStep(ChannelEx* ch)
|
||||
{
|
||||
ch->step.full+=ch->update_rate;
|
||||
ch->step.full += (ch->update_rate * ch->lfo.plfo_step.full) >> 10;
|
||||
fp_22_10 sp=ch->step;
|
||||
ch->step.ip=0;
|
||||
|
||||
|
@ -955,20 +1045,19 @@ void CalcPlfo(ChannelEx* ch)
|
|||
break;
|
||||
|
||||
case 1: // square
|
||||
rv=ch->lfo.state&0x80?0x80:0x7F;
|
||||
rv = ch->lfo.state & 0x80 ? 0xff : 0;
|
||||
break;
|
||||
|
||||
case 2: // triangle
|
||||
rv = (ch->lfo.state & 0x7f) ^ (ch->lfo.state & 0x80 ? 0x7F : 0);
|
||||
rv <<= 1;
|
||||
rv=(u8)(rv-0x80); //2's complement
|
||||
break;
|
||||
|
||||
case 3:// random ! .. not :p
|
||||
rv = (ch->lfo.state >> 3) ^ (ch->lfo.state << 3) ^ (ch->lfo.state & 0xE3);
|
||||
break;
|
||||
}
|
||||
ch->lfo.plfo=rv>>ch->lfo.plfo_shft;
|
||||
ch->lfo.plfo_step.full = ch->lfo.plfo_scale[(u8)rv];
|
||||
}
|
||||
|
||||
template<u32 state>
|
||||
|
@ -1031,8 +1120,54 @@ void AegStep(ChannelEx* ch)
|
|||
template<u32 state>
|
||||
void FegStep(ChannelEx* ch)
|
||||
{
|
||||
|
||||
if (!ch->FEG.active)
|
||||
return;
|
||||
u32 delta;
|
||||
u32 target;
|
||||
switch(state)
|
||||
{
|
||||
case EG_Attack:
|
||||
delta = ch->FEG.AttackRate;
|
||||
target = ch->ccd->FLV1;
|
||||
break;
|
||||
case EG_Decay1:
|
||||
delta = ch->FEG.Decay1Rate;
|
||||
target = ch->ccd->FLV2;
|
||||
break;
|
||||
case EG_Decay2:
|
||||
delta = ch->FEG.Decay2Rate;
|
||||
target = ch->ccd->FLV3;
|
||||
break;
|
||||
case EG_Release:
|
||||
delta = ch->FEG.ReleaseRate;
|
||||
target = ch->ccd->FLV4;
|
||||
break;
|
||||
}
|
||||
target <<= AEG_STEP_BITS;
|
||||
if (ch->FEG.value < target)
|
||||
{
|
||||
u32 maxd = target - ch->FEG.value;
|
||||
if (delta > maxd)
|
||||
delta = maxd;
|
||||
ch->FEG.value += delta;
|
||||
}
|
||||
else if (ch->FEG.value > target)
|
||||
{
|
||||
u32 maxd = ch->FEG.value - target;
|
||||
if (delta > maxd)
|
||||
delta = maxd;
|
||||
ch->FEG.value -= delta;
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
ch->SetFegState((_EG_state)((int)ch->FEG.state + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void staticinitialise()
|
||||
{
|
||||
STREAM_STEP_LUT[0][0][0]=&StreamStep<0,0,0>;
|
||||
|
@ -1138,6 +1273,15 @@ void sgc_Init()
|
|||
Chans[i].Init(i,aica_reg);
|
||||
dsp_out_vol=(DSP_OUT_VOL_REG*)&aica_reg[0x2000];
|
||||
|
||||
for (int s = 0; s < 8; s++)
|
||||
{
|
||||
float limit = PLFOS_Scale[s];
|
||||
for (int i = -128; i < 128; i++)
|
||||
{
|
||||
PLFO_Scales[s][i + 128] = (u32)((1 << 10) * powf(2.0f, limit * i / 128.0f / 1200.0f));
|
||||
}
|
||||
}
|
||||
|
||||
dsp_init();
|
||||
}
|
||||
|
||||
|
@ -1146,9 +1290,9 @@ void sgc_Term()
|
|||
dsp_term();
|
||||
}
|
||||
|
||||
void WriteChannelReg8(u32 channel,u32 reg)
|
||||
void WriteChannelReg(u32 channel, u32 reg, int size)
|
||||
{
|
||||
Chans[channel].RegWrite(reg);
|
||||
Chans[channel].RegWrite(reg, size);
|
||||
}
|
||||
|
||||
void ReadCommonReg(u32 reg,bool byte)
|
||||
|
@ -1425,110 +1569,139 @@ bool channel_serialize(void **data, unsigned int *total_size)
|
|||
|
||||
for ( i = 0 ; i < 64 ; i++)
|
||||
{
|
||||
addr = Chans[i].SA - (&(aica_ram.data[0])) ;
|
||||
addr = Chans[i].SA - (&(aica_ram[0])) ;
|
||||
REICAST_S(addr);
|
||||
|
||||
REICAST_S(Chans[i].CA) ;
|
||||
REICAST_S(Chans[i].step) ;
|
||||
REICAST_S(Chans[i].update_rate) ;
|
||||
REICAST_S(Chans[i].s0) ;
|
||||
REICAST_S(Chans[i].s1) ;
|
||||
REICAST_S(Chans[i].loop) ;
|
||||
REICAST_S(Chans[i].loop.looped) ;
|
||||
REICAST_S(Chans[i].adpcm.last_quant) ;
|
||||
REICAST_S(Chans[i].adpcm.loopstart_quant);
|
||||
REICAST_S(Chans[i].adpcm.loopstart_prev_sample);
|
||||
REICAST_S(Chans[i].adpcm.in_loop);
|
||||
REICAST_S(Chans[i].noise_state) ;
|
||||
REICAST_S(Chans[i].VolMix.DLAtt) ;
|
||||
REICAST_S(Chans[i].VolMix.DRAtt) ;
|
||||
REICAST_S(Chans[i].VolMix.DSPAtt) ;
|
||||
|
||||
addr = Chans[i].VolMix.DSPOut - (&(dsp.MIXS[0])) ;
|
||||
REICAST_S(addr);
|
||||
|
||||
REICAST_S(Chans[i].AEG.val) ;
|
||||
REICAST_S(Chans[i].AEG.state) ;
|
||||
REICAST_S(Chans[i].AEG.AttackRate) ;
|
||||
REICAST_S(Chans[i].AEG.Decay1Rate) ;
|
||||
REICAST_S(Chans[i].AEG.Decay2Rate) ;
|
||||
REICAST_S(Chans[i].AEG.Decay2Value) ;
|
||||
REICAST_S(Chans[i].AEG.ReleaseRate) ;
|
||||
REICAST_S(Chans[i].FEG) ;
|
||||
REICAST_S(Chans[i].step_stream_lut1) ;
|
||||
REICAST_S(Chans[i].step_stream_lut2) ;
|
||||
REICAST_S(Chans[i].step_stream_lut3) ;
|
||||
REICAST_S(Chans[i].FEG.value);
|
||||
REICAST_S(Chans[i].FEG.state);
|
||||
REICAST_S(Chans[i].FEG.prev1);
|
||||
REICAST_S(Chans[i].FEG.prev2);
|
||||
|
||||
REICAST_S(Chans[i].lfo.counter) ;
|
||||
REICAST_S(Chans[i].lfo.start_value) ;
|
||||
REICAST_S(Chans[i].lfo.state) ;
|
||||
REICAST_S(Chans[i].lfo.alfo) ;
|
||||
REICAST_S(Chans[i].lfo.alfo_shft) ;
|
||||
REICAST_S(Chans[i].lfo.plfo) ;
|
||||
REICAST_S(Chans[i].lfo.plfo_shft) ;
|
||||
REICAST_S(Chans[i].lfo.alfo_calc_lut) ;
|
||||
REICAST_S(Chans[i].lfo.plfo_calc_lut) ;
|
||||
REICAST_S(Chans[i].enabled) ;
|
||||
REICAST_S(Chans[i].ChannelNumber) ;
|
||||
}
|
||||
|
||||
/* TODO/FIXME - no possibility for this to return false? */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool channel_unserialize(void **data, unsigned int *total_size)
|
||||
bool channel_unserialize(void **data, unsigned int *total_size, serialize_version_enum ver)
|
||||
{
|
||||
int i = 0 ;
|
||||
int addr = 0 ;
|
||||
u32 dum;
|
||||
|
||||
for ( i = 0 ; i < 64 ; i++)
|
||||
{
|
||||
REICAST_US(addr);
|
||||
Chans[i].SA = addr + (&(aica_ram.data[0])) ;
|
||||
Chans[i].SA = addr + (&(aica_ram[0])) ;
|
||||
|
||||
REICAST_US(Chans[i].CA) ;
|
||||
REICAST_US(Chans[i].step) ;
|
||||
REICAST_US(Chans[i].update_rate) ;
|
||||
if (ver < V7)
|
||||
REICAST_US(dum); // Chans[i].update_rate
|
||||
Chans[i].UpdatePitch();
|
||||
REICAST_US(Chans[i].s0) ;
|
||||
REICAST_US(Chans[i].s1) ;
|
||||
REICAST_US(Chans[i].loop) ;
|
||||
REICAST_US(Chans[i].loop.looped);
|
||||
if (ver < V7)
|
||||
{
|
||||
REICAST_US(dum); // Chans[i].loop.LSA
|
||||
REICAST_US(dum); // Chans[i].loop.LEA
|
||||
}
|
||||
Chans[i].UpdateLoop();
|
||||
REICAST_US(Chans[i].adpcm.last_quant) ;
|
||||
if (ver >= V7)
|
||||
{
|
||||
REICAST_US(Chans[i].adpcm.loopstart_quant);
|
||||
REICAST_US(Chans[i].adpcm.loopstart_prev_sample);
|
||||
REICAST_US(Chans[i].adpcm.in_loop);
|
||||
}
|
||||
else
|
||||
{
|
||||
Chans[i].adpcm.in_loop = true;
|
||||
Chans[i].adpcm.loopstart_quant = 0;
|
||||
Chans[i].adpcm.loopstart_prev_sample = 0;
|
||||
}
|
||||
REICAST_US(Chans[i].noise_state) ;
|
||||
REICAST_US(Chans[i].VolMix.DLAtt) ;
|
||||
REICAST_US(Chans[i].VolMix.DRAtt) ;
|
||||
REICAST_US(Chans[i].VolMix.DSPAtt) ;
|
||||
|
||||
REICAST_US(addr);
|
||||
Chans[i].VolMix.DSPOut = addr + (&(dsp.MIXS[0])) ;
|
||||
if (ver < V7)
|
||||
{
|
||||
REICAST_US(dum); // Chans[i].VolMix.DLAtt
|
||||
REICAST_US(dum); // Chans[i].VolMix.DRAtt
|
||||
REICAST_US(dum); // Chans[i].VolMix.DSPAtt
|
||||
}
|
||||
Chans[i].UpdateAtts();
|
||||
if (ver < V7)
|
||||
REICAST_US(dum); // Chans[i].VolMix.DSPOut
|
||||
Chans[i].UpdateDSPMIX();
|
||||
|
||||
REICAST_US(Chans[i].AEG.val) ;
|
||||
REICAST_US(Chans[i].AEG.state) ;
|
||||
Chans[i].StepAEG=AEG_STEP_LUT[Chans[i].AEG.state];
|
||||
REICAST_US(Chans[i].AEG.AttackRate) ;
|
||||
REICAST_US(Chans[i].AEG.Decay1Rate) ;
|
||||
REICAST_US(Chans[i].AEG.Decay2Rate) ;
|
||||
REICAST_US(Chans[i].AEG.Decay2Value) ;
|
||||
REICAST_US(Chans[i].AEG.ReleaseRate) ;
|
||||
REICAST_US(Chans[i].FEG) ;
|
||||
Chans[i].StepFEG=FEG_STEP_LUT[Chans[i].FEG.state];
|
||||
REICAST_US(Chans[i].step_stream_lut1) ;
|
||||
REICAST_US(Chans[i].step_stream_lut2) ;
|
||||
REICAST_US(Chans[i].step_stream_lut3) ;
|
||||
Chans[i].StepStream=STREAM_STEP_LUT[Chans[i].step_stream_lut1][Chans[i].step_stream_lut2][Chans[i].step_stream_lut3] ;
|
||||
Chans[i].StepStreamInitial=STREAM_INITAL_STEP_LUT[Chans[i].step_stream_lut1];
|
||||
Chans[i].SetAegState(Chans[i].AEG.state);
|
||||
if (ver < V7)
|
||||
{
|
||||
REICAST_US(dum); // Chans[i].AEG.AttackRate
|
||||
REICAST_US(dum); // Chans[i].AEG.Decay1Rate
|
||||
REICAST_US(dum); // Chans[i].AEG.Decay2Rate
|
||||
REICAST_US(dum); // Chans[i].AEG.Decay2Value
|
||||
REICAST_US(dum); // Chans[i].AEG.ReleaseRate
|
||||
}
|
||||
Chans[i].UpdateAEG();
|
||||
REICAST_US(Chans[i].FEG.value);
|
||||
REICAST_US(Chans[i].FEG.state);
|
||||
if (ver >= V7)
|
||||
{
|
||||
REICAST_US(Chans[i].FEG.prev1);
|
||||
REICAST_US(Chans[i].FEG.prev2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Chans[i].FEG.prev1 = 0;
|
||||
Chans[i].FEG.prev2 = 0;
|
||||
}
|
||||
Chans[i].SetFegState(Chans[i].FEG.state);
|
||||
Chans[i].UpdateFEG();
|
||||
if (ver < V7)
|
||||
{
|
||||
u8 dumu8;
|
||||
REICAST_US(dumu8); // Chans[i].step_stream_lut1
|
||||
REICAST_US(dumu8); // Chans[i].step_stream_lut2
|
||||
REICAST_US(dumu8); // Chans[i].step_stream_lut3
|
||||
}
|
||||
Chans[i].UpdateStreamStep();
|
||||
|
||||
REICAST_US(Chans[i].lfo.counter) ;
|
||||
REICAST_US(Chans[i].lfo.start_value) ;
|
||||
if (ver < V7)
|
||||
REICAST_US(dum); // Chans[i].lfo.start_value
|
||||
REICAST_US(Chans[i].lfo.state) ;
|
||||
REICAST_US(Chans[i].lfo.alfo) ;
|
||||
REICAST_US(Chans[i].lfo.alfo_shft) ;
|
||||
REICAST_US(Chans[i].lfo.plfo) ;
|
||||
REICAST_US(Chans[i].lfo.plfo_shft) ;
|
||||
REICAST_US(Chans[i].lfo.alfo_calc_lut) ;
|
||||
REICAST_US(Chans[i].lfo.plfo_calc_lut) ;
|
||||
Chans[i].lfo.alfo_calc = ALFOWS_CALC[Chans[i].lfo.alfo_calc_lut];
|
||||
Chans[i].lfo.plfo_calc = PLFOWS_CALC[Chans[i].lfo.plfo_calc_lut];
|
||||
if (ver < V7)
|
||||
{
|
||||
u8 dumu8;
|
||||
REICAST_US(dumu8); // Chans[i].lfo.alfo
|
||||
REICAST_US(dumu8); // Chans[i].lfo.alfo_shft
|
||||
REICAST_US(dumu8); // Chans[i].lfo.plfo
|
||||
REICAST_US(dumu8); // Chans[i].lfo.plfo_shft
|
||||
REICAST_US(dumu8); // Chans[i].lfo.alfo_calc_lut
|
||||
REICAST_US(dumu8); // Chans[i].lfo.plfo_calc_lut
|
||||
}
|
||||
Chans[i].UpdateLFO();
|
||||
REICAST_US(Chans[i].enabled) ;
|
||||
REICAST_US(Chans[i].ChannelNumber) ;
|
||||
|
||||
if (ver < V7)
|
||||
REICAST_US(dum); // Chans[i].ChannelNumber
|
||||
}
|
||||
|
||||
/* TODO/FIXME - no possibility for this to return false? */
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ void AICA_Sample();
|
|||
void AICA_Sample32();
|
||||
|
||||
//u32 ReadChannelReg(u32 channel,u32 reg);
|
||||
void WriteChannelReg8(u32 channel,u32 reg);
|
||||
void WriteChannelReg(u32 channel, u32 reg, int size);
|
||||
|
||||
void sgc_Init();
|
||||
void sgc_Term();
|
||||
|
@ -56,7 +56,7 @@ typedef s32 SampleType;
|
|||
|
||||
void ReadCommonReg(u32 reg,bool byte);
|
||||
void WriteCommonReg8(u32 reg,u32 data);
|
||||
#define clip(x,min,max) if ((x)<(min)) (x)=(min); if ((x)>(max)) (x)=(max);
|
||||
#define clip(x,min,max) do { if ((x)<(min)) (x)=(min); else if ((x)>(max)) (x)=(max); } while (false)
|
||||
#define clip16(x) clip(x,-32768,32767)
|
||||
bool channel_serialize(void **data, unsigned int *total_size);
|
||||
bool channel_unserialize(void **data, unsigned int *total_size);
|
||||
bool channel_unserialize(void **data, unsigned int *total_size, serialize_version_enum version);
|
||||
|
|
|
@ -30,19 +30,6 @@
|
|||
|
||||
extern "C" void DYNACALL TAWriteSQ(u32 address,u8* sqb);
|
||||
|
||||
enum serialize_version_enum {
|
||||
V1,
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
V5_LIBRETRO_UNSUPPORTED,
|
||||
V6_LIBRETRO_UNSUPPORTED,
|
||||
V7_LIBRETRO,
|
||||
|
||||
V5 = 800,
|
||||
V6 = 801,
|
||||
} ;
|
||||
|
||||
//./core/hw/arm7/arm_mem.cpp
|
||||
extern bool aica_interr;
|
||||
extern u32 aica_reg_L;
|
||||
|
@ -316,7 +303,7 @@ bool dc_serialize(void **data, unsigned int *total_size)
|
|||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
serialize_version_enum version = V6;
|
||||
serialize_version_enum version = V7;
|
||||
|
||||
*total_size = 0 ;
|
||||
|
||||
|
@ -618,7 +605,7 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size)
|
|||
|
||||
REICAST_USA(aica_reg,0x8000);
|
||||
|
||||
channel_unserialize(data, total_size) ;
|
||||
channel_unserialize(data, total_size, V7_LIBRETRO);
|
||||
|
||||
REICAST_USA(cdda_sector,CDDA_SIZE);
|
||||
REICAST_US(cdda_index);
|
||||
|
@ -988,7 +975,7 @@ bool dc_unserialize(void **data, unsigned int *total_size)
|
|||
REICAST_SKIP(2);
|
||||
REICAST_SKIP(2);
|
||||
}
|
||||
channel_unserialize(data, total_size) ;
|
||||
channel_unserialize(data, total_size, version);
|
||||
|
||||
REICAST_USA(cdda_sector,CDDA_SIZE);
|
||||
REICAST_US(cdda_index);
|
||||
|
|
14
core/types.h
14
core/types.h
|
@ -675,3 +675,17 @@ public:
|
|||
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
enum serialize_version_enum {
|
||||
V1,
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
V5_LIBRETRO_UNSUPPORTED,
|
||||
V6_LIBRETRO_UNSUPPORTED,
|
||||
V7_LIBRETRO,
|
||||
|
||||
V5 = 800,
|
||||
V6 = 801,
|
||||
V7 = 802,
|
||||
} ;
|
||||
|
|
Loading…
Reference in New Issue