mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-16 01:35:07 +00:00
Amiga: filter emulation
This commit is contained in:
parent
b514ee30da
commit
154ef3f9a3
6 changed files with 61 additions and 8 deletions
|
@ -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.
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
@ -114,6 +114,8 @@ const char* cmdName[DIV_CMD_MAX]={
|
||||||
|
|
||||||
"SAA_ENVELOPE",
|
"SAA_ENVELOPE",
|
||||||
|
|
||||||
|
"AMIGA_FILTER",
|
||||||
|
|
||||||
"LYNX_LFSR_LOAD",
|
"LYNX_LFSR_LOAD",
|
||||||
|
|
||||||
"QSOUND_ECHO_FEEDBACK",
|
"QSOUND_ECHO_FEEDBACK",
|
||||||
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue