Merge branch 'master' of https://github.com/tildearrow/furnace into ymf278b

This commit is contained in:
cam900 2024-09-05 20:38:25 +09:00
commit bb5d592bb4
108 changed files with 73930 additions and 68431 deletions

View file

@ -137,9 +137,9 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
case 0xf0:
return _("F0xx: Set tick rate (bpm)");
case 0xf1:
return _("F1xx: Single tick note slide up");
return _("F1xx: Single tick pitch up");
case 0xf2:
return _("F2xx: Single tick note slide down");
return _("F2xx: Single tick pitch down");
case 0xf3:
return _("F3xx: Fine volume slide up");
case 0xf4:
@ -151,9 +151,9 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
case 0xf7:
return _("F7xx: Restart macro (see manual)");
case 0xf8:
return _("F8xx: Single tick volume slide up");
return _("F8xx: Single tick volume up");
case 0xf9:
return _("F9xx: Single tick volume slide down");
return _("F9xx: Single tick volume down");
case 0xfa:
return _("FAxx: Fast volume slide (0y: down; x0: up)");
case 0xfc:
@ -520,6 +520,15 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64, bool ol
if (song.subsong[0]->hz<1.0) song.subsong[0]->hz=1.0;
if (song.subsong[0]->hz>999.0) song.subsong[0]->hz=999.0;
curChanMask=c.getIntList("chanMask",{});
for (unsigned char i:curChanMask) {
int j=i-1;
if (j<0) j=0;
if (j>DIV_MAX_CHANS) j=DIV_MAX_CHANS-1;
curSubSong->chanShow[j]=false;
curSubSong->chanShowChanOsc[j]=false;
}
song.author=getConfString("defaultAuthorName","");
}
@ -754,6 +763,13 @@ int DivEngine::addSubSong() {
BUSY_BEGIN;
saveLock.lock();
song.subsong.push_back(new DivSubSong);
for (unsigned char i:curChanMask) {
int j=i-1;
if (j<0) j=0;
if (j>DIV_MAX_CHANS) j=DIV_MAX_CHANS-1;
song.subsong.back()->chanShow[j]=false;
song.subsong.back()->chanShowChanOsc[j]=false;
}
saveLock.unlock();
BUSY_END;
return song.subsong.size()-1;

View file

@ -54,8 +54,8 @@ class DivWorkPool;
#define DIV_UNSTABLE
#define DIV_VERSION "dev217"
#define DIV_ENGINE_VERSION 217
#define DIV_VERSION "dev220"
#define DIV_ENGINE_VERSION 220
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -518,6 +518,7 @@ class DivEngine {
std::vector<DivCommand> cmdStream;
std::vector<DivInstrumentType> possibleInsTypes;
std::vector<DivEffectContainer> effectInst;
std::vector<int> curChanMask;
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];

View file

@ -127,7 +127,6 @@ struct TiunaMatches {
static void writeCmd(std::vector<TiunaBytes>& cmds, TiunaCmd& cmd, unsigned char ch, int& lastWait, int fromTick, int toTick) {
while (fromTick<toTick) {
int val=MIN(toTick-fromTick,256);
assert(val>0);
if (lastWait!=val) {
cmd.wait=val;
lastWait=val;
@ -504,12 +503,6 @@ void DivExportTiuna::run() {
running=false;
return;
}
SafeWriter dbg;
dbg.init();
dbg.writeText(fmt::format("renderedCmds size={}\n",renderedCmds.size()));
for (const auto& i: confirmedMatches) {
dbg.writeText(fmt::format("pos={},end={},id={}\n",i.pos,i.endPos,i.id,i.size));
}
// write commands
int totalSize=0;

View file

@ -674,7 +674,10 @@ void DivExportZSM::run() {
if (writes.size()>0)
logD("zsmOps: Writing %d messages to chip %d",writes.size(),i);
for (DivRegWrite& write: writes) {
if (i==YM) zsm.writeYM(write.addr&0xff,write.val);
if (i==YM) {
if (done && write.addr==0x08 && (write.val&0x78)>0) continue; // don't process keydown on lookahead
zsm.writeYM(write.addr&0xff,write.val);
}
if (i==VERA) {
if (done && write.addr>=64) continue; // don't process any PCM or sync events on the loop lookahead
zsm.writePSG(write.addr&0xff,write.val);

View file

@ -2102,6 +2102,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
}
}
// SNES no anti-click
if (ds.version<220) {
for (int i=0; i<ds.systemLen; i++) {
if (ds.system[i]==DIV_SYSTEM_SNES) {
ds.systemFlags[i].set("antiClick",false);
}
}
}
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();

View file

@ -762,7 +762,20 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_OPMASK:
chan[c.chan].opMask=c.value&15;
switch (c.value>>4) {
case 1:
case 2:
case 3:
case 4:
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
if (c.value&15) {
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
}
break;
default:
chan[c.chan].opMask=c.value&15;
break;
}
if (chan[c.chan].active) {
chan[c.chan].opMaskChanged=true;
}

View file

@ -113,14 +113,15 @@ const unsigned char dacLogTableAY[256]={
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
};
void DivPlatformAY8910::runDAC() {
void DivPlatformAY8910::runDAC(int runRate) {
if (runRate==0) runRate=dacRate;
for (int i=0; i<3; i++) {
if (chan[i].active && (chan[i].curPSGMode.val&8) && chan[i].dac.sample!=-1) {
chan[i].dac.period+=chan[i].dac.rate;
bool end=false;
bool changed=false;
int prevOut=chan[i].dac.out;
while (chan[i].dac.period>dacRate && !end) {
while (chan[i].dac.period>runRate && !end) {
DivSample* s=parent->getSample(chan[i].dac.sample);
if (s->samples<=0 || chan[i].dac.pos<0 || chan[i].dac.pos>=(int)s->samples) {
chan[i].dac.sample=-1;
@ -143,7 +144,7 @@ void DivPlatformAY8910::runDAC() {
end=true;
break;
}
chan[i].dac.period-=dacRate;
chan[i].dac.period-=runRate;
}
if (changed && !end) {
if (!isMuted[i]) {
@ -154,13 +155,15 @@ void DivPlatformAY8910::runDAC() {
}
}
void DivPlatformAY8910::runTFX() {
void DivPlatformAY8910::runTFX(int runRate) {
/*
developer's note: if you are checking for intellivision
make sure to add "&& selCore"
because for some reason, the register remap doesn't work
when the user uses AtomicSSG core
*/
float counterRatio=1.0;
if (runRate!=0) counterRatio=(double)rate/(double)runRate;
int timerPeriod, output;
for (int i=0; i<3; i++) {
if (chan[i].active && (chan[i].curPSGMode.val&16) && !(chan[i].curPSGMode.val&8) && chan[i].tfx.mode!=-1) {
@ -182,9 +185,9 @@ void DivPlatformAY8910::runTFX() {
continue;
}
}
chan[i].tfx.counter += 1;
chan[i].tfx.counter += counterRatio;
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 0) {
chan[i].tfx.counter = 0;
chan[i].tfx.counter -= chan[i].tfx.period;
chan[i].tfx.out ^= 1;
output = ((chan[i].tfx.out) ? chan[i].outVol : (chan[i].tfx.lowBound-(15-chan[i].outVol)));
// TODO: fix this stupid crackling noise that happens
@ -201,7 +204,7 @@ void DivPlatformAY8910::runTFX() {
}
}
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 1) {
chan[i].tfx.counter = 0;
chan[i].tfx.counter -= chan[i].tfx.period;
if (!isMuted[i]) {
if (intellivision && selCore) {
immWrite(0xa, ayEnvMode);
@ -211,7 +214,7 @@ void DivPlatformAY8910::runTFX() {
}
}
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 2) {
chan[i].tfx.counter = 0;
chan[i].tfx.counter -= chan[i].tfx.period;
}
}
if (chan[i].tfx.num > 0) {
@ -327,12 +330,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
void DivPlatformAY8910::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
writes.clear();
int rate=(int)(chipClock/sRate);
for (size_t i=0; i<len; i++) {
for (int h=0; h<rate; h++) {
runDAC();
runTFX();
}
runDAC(sRate);
runTFX(sRate);
while (!writes.empty()) {
QueuedWrite& w=writes.front();
stream.push_back(DivDelayedWrite(i,w.addr,w.val));

View file

@ -78,7 +78,9 @@ class DivPlatformAY8910: public DivDispatch {
} dac;
struct TFX {
int period, counter, offset, den, num, mode, lowBound, out;
int period;
float counter;
int offset, den, num, mode, lowBound, out;
TFX():
period(0),
counter(0),
@ -156,8 +158,8 @@ class DivPlatformAY8910: public DivDispatch {
friend void putDispatchChan(void*,int,int);
public:
void runDAC();
void runTFX();
void runDAC(int runRate=0);
void runTFX(int runRate=0);
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
void acquire(short** buf, size_t len);
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);

View file

@ -1465,7 +1465,20 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
case DIV_CMD_FM_OPMASK:
if (c.chan>=psgChanOffs) break;
chan[c.chan].opMask=c.value&15;
switch (c.value>>4) {
case 1:
case 2:
case 3:
case 4:
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
if (c.value&15) {
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
}
break;
default:
chan[c.chan].opMask=c.value&15;
break;
}
if (chan[c.chan].active) {
chan[c.chan].opMaskChanged=true;
}

View file

@ -175,6 +175,13 @@ void DivPlatformNES::acquire_NSFPlayE(short** buf, size_t len) {
int out2[2];
for (size_t i=0; i<len; i++) {
doPCM;
if (!writes.empty()) {
QueuedWrite w=writes.front();
doWrite(w.addr,w.val);
regPool[w.addr&0x1f]=w.val;
writes.pop();
}
e1_NP->Tick(8);
e2_NP->TickFrameSequence(8);
@ -436,7 +443,7 @@ void DivPlatformNES::tick(bool sysTick) {
// https://www.youtube.com/watch?v=vB4P8x2Am6Y
if (lsamp->loopEnd>lsamp->loopStart && goingToLoop) {
int loopStartAddr=(sampleOffDPCM[dacSample]+lsamp->loopStart)>>3;
int loopStartAddr=sampleOffDPCM[dacSample]+(lsamp->loopStart>>3);
int loopLen=(lsamp->loopEnd-lsamp->loopStart)>>3;
rWrite(0x4012,(loopStartAddr>>6)&0xff);
@ -492,14 +499,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
}
}
if (c.value!=DIV_NOTE_NULL) {
dacSample=ins->amiga.getSample(c.value);
dacSample=(int)ins->amiga.getSample(c.value);
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].sampleNote=c.value;
c.value=ins->amiga.getFreq(c.value);
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
}
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
dacSample=(int)ins->amiga.getSample(chan[c.chan].sampleNote);
if (ins->type==DIV_INS_AMIGA) {
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
}

View file

@ -27,6 +27,8 @@
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) {rWrite((a)+(c)*16,v)}
#define rWriteDelay(a,v,d) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,d)); if (dumpWrites) {addWrite(a,v);} }
#define chWriteDelay(c,a,v,d) {rWrite((a)+(c)*16,v,d)}
#define sampleTableAddr(c) (sampleTableBase+(c)*4)
#define waveTableAddr(c) (sampleTableBase+8*4+(c)*9*16)
@ -77,7 +79,7 @@ void DivPlatformSNES::acquire(short** buf, size_t len) {
dsp.write(w.addr,w.val);
regPool[w.addr&0x7f]=w.val;
writes.pop();
delay=(w.addr==0x5c)?8:1;
delay=w.delay;
}
}
dsp.set_output(out,1);
@ -253,7 +255,18 @@ void DivPlatformSNES::tick(bool sysTick) {
}
}
if (koff!=0) {
rWrite(0x5c,koff);
// TODO: improve
if (antiClick) {
for (int i=0; i<8; i++) {
if (koff&(1<<i)) {
chWrite(i,5,0);
chWrite(i,7,0x9f);
chan[i].shallWriteEnv=true;
}
}
rWriteDelay(0x7e,0,64);
}
rWriteDelay(0x5c,koff,8);
}
if (writeControl) {
unsigned char control=(noiseFreq&0x1f)|(echoOn?0:0x20);
@ -314,10 +327,7 @@ void DivPlatformSNES::tick(bool sysTick) {
}
}
if (koff!=0) {
rWrite(0x5c,0);
}
if (kon!=0) {
rWrite(0x4c,kon);
rWriteDelay(0x5c,0,8);
}
for (int i=0; i<8; i++) {
if (chan[i].shallWriteVol) {
@ -325,6 +335,9 @@ void DivPlatformSNES::tick(bool sysTick) {
chan[i].shallWriteVol=false;
}
}
if (kon!=0) {
rWrite(0x4c,kon);
}
}
int DivPlatformSNES::dispatch(DivCommand c) {
@ -1027,6 +1040,7 @@ void DivPlatformSNES::setFlags(const DivConfig& flags) {
initEchoMask=flags.getInt("echoMask",0);
interpolationOff=flags.getBool("interpolationOff",false);
antiClick=flags.getBool("antiClick",true);
}
int DivPlatformSNES::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {

View file

@ -70,6 +70,7 @@ class DivPlatformSNES: public DivDispatch {
bool writeDryVol;
bool echoOn;
bool interpolationOff;
bool antiClick;
bool initEchoOn;
signed char initEchoVolL;
@ -82,8 +83,10 @@ class DivPlatformSNES: public DivDispatch {
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
unsigned char delay;
unsigned char padding;
QueuedWrite(): addr(0), val(0), delay(0), padding(0) {}
QueuedWrite(unsigned char a, unsigned char v, unsigned char d=0): addr(a), val(v), delay(d), padding(0) {}
};
FixedQueue<QueuedWrite,256> writes;

View file

@ -173,6 +173,7 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
@ -255,6 +256,7 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
@ -311,6 +313,16 @@ void DivPlatformYM2203::acquire_lle(short** buf, size_t len) {
fmOut[i]=0;
}
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
immWrite(i.addr&15,i.val);
}
ay->getRegisterWrites().clear();
while (true) {
bool canWeWrite=fm_lle.prescaler_latch[1]&1;
@ -444,6 +456,10 @@ void DivPlatformYM2203::acquire_lle(short** buf, size_t len) {
}
}
void DivPlatformYM2203::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
ay->fillStream(stream,sRate,len);
}
void DivPlatformYM2203::tick(bool sysTick) {
// PSG
ay->tick(sysTick);
@ -1006,7 +1022,20 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
}
case DIV_CMD_FM_OPMASK:
if (c.chan>=psgChanOffs) break;
chan[c.chan].opMask=c.value&15;
switch (c.value>>4) {
case 1:
case 2:
case 3:
case 4:
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
if (c.value&15) {
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
}
break;
default:
chan[c.chan].opMask=c.value&15;
break;
}
if (chan[c.chan].active) {
chan[c.chan].opMaskChanged=true;
}

View file

@ -74,6 +74,7 @@ class DivPlatformYM2203: public DivPlatformOPN {
public:
void acquire(short** buf, size_t len);
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);

View file

@ -325,6 +325,7 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
@ -440,6 +441,7 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
@ -680,6 +682,10 @@ void DivPlatformYM2608::acquire_lle(short** buf, size_t len) {
}
}
void DivPlatformYM2608::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
ay->fillStream(stream,sRate,len);
}
void DivPlatformYM2608::tick(bool sysTick) {
// FM
for (int i=0; i<6; i++) {
@ -1539,7 +1545,20 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
}
case DIV_CMD_FM_OPMASK:
if (c.chan>=psgChanOffs) break;
chan[c.chan].opMask=c.value&15;
switch (c.value>>4) {
case 1:
case 2:
case 3:
case 4:
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
if (c.value&15) {
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
}
break;
default:
chan[c.chan].opMask=c.value&15;
break;
}
if (chan[c.chan].active) {
chan[c.chan].opMaskChanged=true;
}

View file

@ -93,6 +93,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
public:
void acquire(short** buf, size_t len);
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);

View file

@ -260,6 +260,7 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
@ -373,6 +374,7 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
@ -1509,7 +1511,20 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
}
case DIV_CMD_FM_OPMASK:
if (c.chan>=psgChanOffs) break;
chan[c.chan].opMask=c.value&15;
switch (c.value>>4) {
case 1:
case 2:
case 3:
case 4:
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
if (c.value&15) {
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
}
break;
default:
chan[c.chan].opMask=c.value&15;
break;
}
if (chan[c.chan].active) {
chan[c.chan].opMaskChanged=true;
}

View file

@ -324,6 +324,7 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
@ -439,6 +440,7 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
// AY -> OPN
ay->runDAC();
ay->runTFX(rate);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
if (i.addr>15) continue;
@ -1578,7 +1580,20 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
}
case DIV_CMD_FM_OPMASK:
if (c.chan>=psgChanOffs) break;
chan[c.chan].opMask=c.value&15;
switch (c.value>>4) {
case 1:
case 2:
case 3:
case 4:
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
if (c.value&15) {
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
}
break;
default:
chan[c.chan].opMask=c.value&15;
break;
}
if (chan[c.chan].active) {
chan[c.chan].opMaskChanged=true;
}

View file

@ -108,6 +108,10 @@ class DivPlatformYM2610Base: public DivPlatformOPN {
}
public:
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
ay->fillStream(stream,sRate,len);
}
void reset() {
writeADPCMAOff=0;
writeADPCMAOn=0;