Amiga: filter emulation

This commit is contained in:
tildearrow 2022-03-26 23:39:20 -05:00
parent b514ee30da
commit 154ef3f9a3
6 changed files with 61 additions and 8 deletions

View file

@ -6,4 +6,4 @@ in this very computer music trackers were born...
# effects # effects
none. as of this moment the Amiga doesn't need any effects in particular, but some may be added in a future. - `10xx`: toggle low-pass filter. `0` turns it off and `1` turns it on.

View file

@ -106,6 +106,8 @@ enum DivDispatchCmds {
DIV_CMD_SAA_ENVELOPE, DIV_CMD_SAA_ENVELOPE,
DIV_CMD_AMIGA_FILTER,
DIV_CMD_LYNX_LFSR_LOAD, DIV_CMD_LYNX_LFSR_LOAD,
DIV_CMD_QSOUND_ECHO_FEEDBACK, DIV_CMD_QSOUND_ECHO_FEEDBACK,

View file

@ -1573,6 +1573,9 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
fxTyp=fxVal>>4; fxTyp=fxVal>>4;
fxVal&=0x0f; fxVal&=0x0f;
switch (fxTyp) { switch (fxTyp) {
case 0:
writeFxCol(0x10,!fxVal);
break;
case 1: // single note slide up case 1: // single note slide up
case 2: // single note slide down case 2: // single note slide down
writeFxCol(fxTyp-1+0xf1,fxVal); writeFxCol(fxTyp-1+0xf1,fxVal);
@ -1613,7 +1616,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
ds.systemLen=(chCount+3)/4; ds.systemLen=(chCount+3)/4;
for(int i=0; i<ds.systemLen; i++) { for(int i=0; i<ds.systemLen; i++) {
ds.system[i]=DIV_SYSTEM_AMIGA; ds.system[i]=DIV_SYSTEM_AMIGA;
ds.systemFlags[i]=1|(80<<8)|(bypassLimits?4:0); // PAL ds.systemFlags[i]=1|(80<<8)|(bypassLimits?4:0)|((ds.systemLen>1 || bypassLimits)?2:0); // PAL
} }
for(int i=0; i<chCount; i++) { for(int i=0; i<chCount; i++) {
ds.chanShow[i]=true; ds.chanShow[i]=true;

View file

@ -63,10 +63,20 @@ const char** DivPlatformAmiga::getRegisterSheet() {
return regCheatSheetAmiga; return regCheatSheetAmiga;
} }
const char* DivPlatformAmiga::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Toggle filter (0 disables; 1 enables)";
break;
}
return NULL;
}
void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t len) { void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t len) {
static int outL, outR;
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
bufL[h]=0; outL=0;
bufR[h]=0; outR=0;
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) { if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
chan[i].audSub-=AMIGA_DIVIDER; chan[i].audSub-=AMIGA_DIVIDER;
@ -102,14 +112,20 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le
} }
if (!isMuted[i]) { if (!isMuted[i]) {
if (i==0 || i==3) { if (i==0 || i==3) {
bufL[h]+=((chan[i].audDat*chan[i].outVol)*sep1)>>7; outL+=((chan[i].audDat*chan[i].outVol)*sep1)>>7;
bufR[h]+=((chan[i].audDat*chan[i].outVol)*sep2)>>7; outR+=((chan[i].audDat*chan[i].outVol)*sep2)>>7;
} else { } else {
bufL[h]+=((chan[i].audDat*chan[i].outVol)*sep2)>>7; outL+=((chan[i].audDat*chan[i].outVol)*sep2)>>7;
bufR[h]+=((chan[i].audDat*chan[i].outVol)*sep1)>>7; outR+=((chan[i].audDat*chan[i].outVol)*sep1)>>7;
} }
} }
} }
filter[0][0]+=(filtConst*(outL-filter[0][0]))>>12;
filter[0][1]+=(filtConst*(filter[0][0]-filter[0][1]))>>12;
filter[1][0]+=(filtConst*(outR-filter[1][0]))>>12;
filter[1][1]+=(filtConst*(filter[1][0]-filter[1][1]))>>12;
bufL[h]=filter[0][1];
bufR[h]=filter[1][1];
} }
} }
@ -299,6 +315,10 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
chan[c.chan].audPos=c.value; chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true; chan[c.chan].setPos=true;
break; break;
case DIV_CMD_AMIGA_FILTER:
filterOn=c.value;
filtConst=filterOn?filtConstOn:filtConstOff;
break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:
return 64; return 64;
break; break;
@ -332,7 +352,11 @@ void* DivPlatformAmiga::getChanState(int ch) {
void DivPlatformAmiga::reset() { void DivPlatformAmiga::reset() {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]=DivPlatformAmiga::Channel(); chan[i]=DivPlatformAmiga::Channel();
filter[0][i]=0;
filter[1][i]=0;
} }
filterOn=false;
filtConst=filterOn?filtConstOn:filtConstOff;
} }
bool DivPlatformAmiga::isStereo() { bool DivPlatformAmiga::isStereo() {
@ -372,6 +396,13 @@ void DivPlatformAmiga::setFlags(unsigned int flags) {
sep2=127-((flags>>8)&127); sep2=127-((flags>>8)&127);
amigaModel=flags&2; amigaModel=flags&2;
bypassLimits=flags&4; bypassLimits=flags&4;
if (amigaModel) {
filtConstOff=4000;
filtConstOn=sin(M_PI*8000.0/(double)rate)*4096.0;
} else {
filtConstOff=sin(M_PI*16000.0/(double)rate)*4096.0;
filtConstOn=sin(M_PI*5500.0/(double)rate)*4096.0;
}
} }
int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {

View file

@ -68,6 +68,11 @@ class DivPlatformAmiga: public DivDispatch {
bool isMuted[4]; bool isMuted[4];
bool bypassLimits; bool bypassLimits;
bool amigaModel; bool amigaModel;
bool filterOn;
int filter[2][4];
int filtConst;
int filtConstOff, filtConstOn;
int sep1, sep2; int sep1, sep2;
@ -88,6 +93,7 @@ class DivPlatformAmiga: public DivDispatch {
void notifyWaveChange(int wave); void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins); void notifyInsDeletion(void* ins);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
}; };

View file

@ -113,6 +113,8 @@ const char* cmdName[DIV_CMD_MAX]={
"AY_AUTO_PWM", "AY_AUTO_PWM",
"SAA_ENVELOPE", "SAA_ENVELOPE",
"AMIGA_FILTER",
"LYNX_LFSR_LOAD", "LYNX_LFSR_LOAD",
@ -649,6 +651,15 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
return false; return false;
} }
break; break;
case DIV_SYSTEM_AMIGA:
switch (effect) {
case 0x10: // toggle filter
dispatchCmd(DivCommand(DIV_CMD_AMIGA_FILTER,ch,effectVal));
break;
default:
return false;
}
break;
case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT: case DIV_SYSTEM_SEGAPCM_COMPAT:
switch (effect) { switch (effect) {