greatly improved playback resume

no more song weirding out when changing orders
This commit is contained in:
tildearrow 2021-12-21 01:29:07 -05:00
parent 84f36a8c66
commit 997c460fc1
24 changed files with 243 additions and 120 deletions

View File

@ -95,6 +95,10 @@ class DivEngine;
class DivDispatch {
protected:
DivEngine* parent;
/**
* please honor this variable if needed.
*/
bool skipRegisterWrites;
public:
/**
* the rate the samples are provided.
@ -167,6 +171,16 @@ class DivDispatch {
*/
virtual void setPAL(bool pal);
/**
* set skip reg writes.
*/
void setSkipRegisterWrites(bool value);
/**
* force-retrigger instruments.
*/
virtual void forceIns();
/**
* initialize this DivDispatch.
* @param parent the parent DivEngine.

View File

@ -1419,15 +1419,30 @@ void DivEngine::setLoops(int loops) {
remainingLoops=loops;
}
void DivEngine::play() {
isBusy.lock();
void DivEngine::playSub() {
reset();
int goal=curOrder;
curOrder=0;
curRow=0;
clockDrift=0;
cycles=0;
ticks=1;
speedAB=false;
playing=true;
dispatch->setSkipRegisterWrites(true);
while (curOrder<goal) {
if (nextTick()) break;
}
dispatch->setSkipRegisterWrites(false);
dispatch->forceIns();
clockDrift=0;
cycles=0;
ticks=1;
}
void DivEngine::play() {
isBusy.lock();
playSub();
isBusy.unlock();
}
@ -1667,12 +1682,7 @@ void DivEngine::setOrder(unsigned char order) {
curOrder=order;
if (order>=song.ordersLen) curOrder=0;
if (playing) {
reset();
curRow=0;
clockDrift=0;
cycles=0;
ticks=1;
speedAB=false;
playSub();
}
isBusy.unlock();
}

View File

@ -113,6 +113,7 @@ class DivEngine {
bool perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal);
void renderSamples();
void reset();
void playSub();
public:
DivSong song;

View File

@ -34,6 +34,14 @@ bool DivDispatch::keyOffAffectsArp(int ch) {
void DivDispatch::setPAL(bool pal) {
}
void DivDispatch::setSkipRegisterWrites(bool value) {
skipRegisterWrites=value;
}
void DivDispatch::forceIns() {
}
int DivDispatch::init(DivEngine* p, int channels, int sugRate, bool pal) {
return 0;
}

View File

@ -31,7 +31,8 @@ static int orderedOps[4]={
0,2,1,3
};
#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v);}
void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static int o[2];
@ -167,14 +168,14 @@ int hScale(int note) {
void DivPlatformArcade::tick() {
for (int i=0; i<8; i++) {
if (chan[i].keyOn || chan[i].keyOff) {
writes.emplace(0x08,i);
immWrite(0x08,i);
chan[i].keyOff=false;
}
}
for (int i=0; i<256; i++) {
if (pendingWrites[i]!=oldWrites[i]) {
writes.emplace(i,pendingWrites[i]&0xff);
immWrite(i,pendingWrites[i]&0xff);
oldWrites[i]=pendingWrites[i];
}
}
@ -182,12 +183,12 @@ void DivPlatformArcade::tick() {
for (int i=0; i<8; i++) {
if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64;
writes.emplace(i+0x28,hScale(chan[i].freq>>6));
writes.emplace(i+0x30,chan[i].freq<<2);
immWrite(i+0x28,hScale(chan[i].freq>>6));
immWrite(i+0x30,chan[i].freq<<2);
chan[i].freqChanged=false;
}
if (chan[i].keyOn) {
writes.emplace(0x08,0x78|i);
immWrite(0x08,0x78|i);
chan[i].keyOn=false;
}
}
@ -209,6 +210,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
if (c.chan>7) {
if (skipRegisterWrites) break;
chan[c.chan].pcm.sample=12*sampleBank+c.value%12;
if (chan[c.chan].pcm.sample>=parent->song.sampleLen) {
chan[c.chan].pcm.sample=-1;
@ -425,6 +427,12 @@ int DivPlatformArcade::dispatch(DivCommand c) {
return 1;
}
void DivPlatformArcade::forceIns() {
for (int i=0; i<13; i++) {
chan[i].insChanged=true;
}
}
void DivPlatformArcade::reset() {
while (!writes.empty()) writes.pop();
if (useYMFM) {
@ -451,8 +459,8 @@ void DivPlatformArcade::reset() {
delay=0;
//rWrite(0x18,0x10);
writes.emplace(0x19,0x7f);
writes.emplace(0x19,0xff);
immWrite(0x19,0x7f);
immWrite(0x19,0xff);
//rWrite(0x1b,0x00);
extMode=false;

View File

@ -64,6 +64,7 @@ class DivPlatformArcade: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
bool isStereo();

View File

@ -4,6 +4,8 @@
#define FREQ_BASE 277.0f
#define rWrite(a,v) if (!skipRegisterWrites) {sid.write(a,v);}
void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
sid.clock();
@ -12,10 +14,10 @@ void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len)
}
void DivPlatformC64::updateFilter() {
sid.write(0x15,filtCut&7);
sid.write(0x16,filtCut>>3);
sid.write(0x17,(filtRes<<4)|(chan[2].filter<<2)|(chan[1].filter<<1)|(chan[0].filter));
sid.write(0x18,(filtControl<<4)|vol);
rWrite(0x15,filtCut&7);
rWrite(0x16,filtCut>>3);
rWrite(0x17,(filtRes<<4)|(chan[2].filter<<2)|(chan[1].filter<<1)|(chan[0].filter));
rWrite(0x18,(filtControl<<4)|vol);
}
void DivPlatformC64::tick() {
@ -50,37 +52,37 @@ void DivPlatformC64::tick() {
}
if (chan[i].std.hadDuty) {
chan[i].duty-=((signed char)chan[i].std.duty-12)*4;
sid.write(i*7+2,chan[i].duty&0xff);
sid.write(i*7+3,chan[i].duty>>8);
rWrite(i*7+2,chan[i].duty&0xff);
rWrite(i*7+3,chan[i].duty>>8);
}
if (chan[i].testWhen>0) {
if (--chan[i].testWhen<1) {
if (!chan[i].resetMask && !isMuted[i]) {
sid.write(i*7+5,0);
sid.write(i*7+6,0);
sid.write(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|8|(chan[i].ring<<2)|(chan[i].sync<<1));
rWrite(i*7+5,0);
rWrite(i*7+6,0);
rWrite(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|8|(chan[i].ring<<2)|(chan[i].sync<<1));
}
}
}
if (chan[i].std.hadWave) {
chan[i].wave=chan[i].std.wave;
sid.write(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|chan[i].active);
rWrite(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|chan[i].active);
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE+chan[i].pitch))/ONE_SEMITONE;
if (chan[i].freq>0xffff) chan[i].freq=0xffff;
if (chan[i].keyOn) {
sid.write(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
sid.write(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
sid.write(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|1);
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
rWrite(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|1);
}
if (chan[i].keyOff && !isMuted[i]) {
sid.write(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
sid.write(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
sid.write(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|0);
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
rWrite(i*7+4,(isMuted[i]?0:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|0);
}
sid.write(i*7,chan[i].freq&0xff);
sid.write(i*7+1,chan[i].freq>>8);
rWrite(i*7,chan[i].freq&0xff);
rWrite(i*7+1,chan[i].freq>>8);
if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false;
@ -99,8 +101,8 @@ int DivPlatformC64::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacroLen>0) {
chan[c.chan].duty=(ins->c64.duty*4095)/100;
sid.write(c.chan*7+2,chan[c.chan].duty&0xff);
sid.write(c.chan*7+3,chan[c.chan].duty>>8);
rWrite(c.chan*7+2,chan[c.chan].duty&0xff);
rWrite(c.chan*7+3,chan[c.chan].duty>>8);
}
if (chan[c.chan].insChanged) {
chan[c.chan].wave=(ins->c64.noiseOn<<3)|(ins->c64.pulseOn<<2)|(ins->c64.sawOn<<1)|(ins->c64.triOn);
@ -181,12 +183,12 @@ int DivPlatformC64::dispatch(DivCommand c) {
}
case DIV_CMD_STD_NOISE_MODE:
chan[c.chan].duty=(c.value*4095)/100;
sid.write(c.chan*7+2,chan[c.chan].duty&0xff);
sid.write(c.chan*7+3,chan[c.chan].duty>>8);
rWrite(c.chan*7+2,chan[c.chan].duty&0xff);
rWrite(c.chan*7+3,chan[c.chan].duty>>8);
break;
case DIV_CMD_WAVE:
chan[c.chan].wave=c.value;
sid.write(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active);
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active);
break;
case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=round(FREQ_BASE*pow(2.0f,((float)(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp-12):(0)))/12.0f)));
@ -236,8 +238,8 @@ int DivPlatformC64::dispatch(DivCommand c) {
if (c.value&15) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
chan[c.chan].duty=(ins->c64.duty*4095)/100;
sid.write(c.chan*7+2,chan[c.chan].duty&0xff);
sid.write(c.chan*7+3,chan[c.chan].duty>>8);
rWrite(c.chan*7+2,chan[c.chan].duty&0xff);
rWrite(c.chan*7+3,chan[c.chan].duty>>8);
}
chan[c.chan].resetDuty=c.value>>4;
break;
@ -257,11 +259,11 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case 4:
chan[c.chan].ring=c.value;
sid.write(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active);
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active);
break;
case 5:
chan[c.chan].sync=c.value;
sid.write(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active);
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active);
break;
case 6:
filtControl&=7;
@ -280,7 +282,14 @@ int DivPlatformC64::dispatch(DivCommand c) {
void DivPlatformC64::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
sid.write(ch*7+4,(isMuted[ch]?0:(chan[ch].wave<<4))|(chan[ch].ring<<2)|(chan[ch].sync<<1)|chan[ch].active);
rWrite(ch*7+4,(isMuted[ch]?0:(chan[ch].wave<<4))|(chan[ch].ring<<2)|(chan[ch].sync<<1)|chan[ch].active);
}
void DivPlatformC64::forceIns() {
for (int i=0; i<3; i++) {
chan[i].insChanged=true;
chan[i].testWhen=0;
}
}
void DivPlatformC64::reset() {
@ -290,7 +299,7 @@ void DivPlatformC64::reset() {
sid.reset();
sid.write(0x18,0x0f);
rWrite(0x18,0x0f);
filtControl=0;
filtRes=0;

View File

@ -57,6 +57,7 @@ class DivPlatformC64: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
void setPAL(bool pal);

View File

@ -3,7 +3,7 @@
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) GB_apu_write(gb,a,v);
#define rWrite(a,v) if (!skipRegisterWrites) {GB_apu_write(gb,a,v);}
#define FREQ_BASE 8015.85f
@ -286,6 +286,14 @@ int DivPlatformGB::dispatch(DivCommand c) {
return 1;
}
void DivPlatformGB::forceIns() {
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
}
GB_apu_write(gb,0x25,procMute());
updateWave();
}
void DivPlatformGB::reset() {
for (int i=0; i<4; i++) {
chan[i]=DivPlatformGB::Channel();

View File

@ -41,6 +41,7 @@ class DivPlatformGB: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
bool isStereo();

View File

@ -23,9 +23,9 @@ void DivPlatformGenesis::acquire(short* bufL, short* bufR, size_t start, size_t
DivSample* s=parent->song.sample[dacSample];
if (!isMuted[5]) {
if (s->depth==8) {
writes.emplace(0x2a,(unsigned char)s->rendData[dacPos++]+0x80);
immWrite(0x2a,(unsigned char)s->rendData[dacPos++]+0x80);
} else {
writes.emplace(0x2a,((unsigned short)s->rendData[dacPos++]+0x8000)>>8);
immWrite(0x2a,((unsigned short)s->rendData[dacPos++]+0x8000)>>8);
}
}
if (dacPos>=s->rendLength) {
@ -82,14 +82,14 @@ void DivPlatformGenesis::tick() {
for (int i=0; i<6; i++) {
if (i==2 && extMode) continue;
if (chan[i].keyOn || chan[i].keyOff) {
writes.emplace(0x28,0x00|konOffs[i]);
immWrite(0x28,0x00|konOffs[i]);
chan[i].keyOff=false;
}
}
for (int i=0; i<512; i++) {
if (pendingWrites[i]!=oldWrites[i]) {
writes.emplace(i,pendingWrites[i]&0xff);
immWrite(i,pendingWrites[i]&0xff);
oldWrites[i]=pendingWrites[i];
}
}
@ -99,12 +99,12 @@ void DivPlatformGenesis::tick() {
if (chan[i].freqChanged) {
chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE+chan[i].pitch))/ONE_SEMITONE;
int freqt=toFreq(chan[i].freq);
writes.emplace(chanOffs[i]+0xa4,freqt>>8);
writes.emplace(chanOffs[i]+0xa0,freqt&0xff);
immWrite(chanOffs[i]+0xa4,freqt>>8);
immWrite(chanOffs[i]+0xa0,freqt&0xff);
chan[i].freqChanged=false;
}
if (chan[i].keyOn) {
writes.emplace(0x28,0xf0|konOffs[i]);
immWrite(0x28,0xf0|konOffs[i]);
chan[i].keyOn=false;
}
}
@ -171,6 +171,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
if (c.chan==5 && dacMode) {
if (skipRegisterWrites) break;
dacSample=12*sampleBank+c.value%12;
if (dacSample>=parent->song.sampleLen) {
dacSample=-1;
@ -370,6 +371,15 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
return 1;
}
void DivPlatformGenesis::forceIns() {
for (int i=0; i<10; i++) {
chan[i].insChanged=true;
}
if (dacMode) {
rWrite(0x2b,0x80);
}
}
void DivPlatformGenesis::reset() {
while (!writes.empty()) writes.pop();
OPN2_Reset(&fm);
@ -394,7 +404,7 @@ void DivPlatformGenesis::reset() {
extMode=false;
// LFO
writes.emplace(0x22,0x08);
immWrite(0x22,0x08);
delay=0;

View File

@ -53,6 +53,7 @@ class DivPlatformGenesis: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
bool isStereo();

View File

@ -231,7 +231,7 @@ void DivPlatformGenesisExt::tick() {
}
}
if (writeSomething) {
writes.emplace(0x28,writeMask);
immWrite(0x28,writeMask);
}
}
@ -267,8 +267,8 @@ void DivPlatformGenesisExt::tick() {
opChan[i].freqH=(opChan[i].freq>>8)&7;
opChan[i].freqL=opChan[i].freq&0xff;
}
writes.emplace(opChanOffsH[i],opChan[i].freqH);
writes.emplace(opChanOffsL[i],opChan[i].freqL);
immWrite(opChanOffsH[i],opChan[i].freqH);
immWrite(opChanOffsL[i],opChan[i].freqL);
}
writeMask|=opChan[i].active<<(4+i);
if (opChan[i].keyOn) {
@ -278,7 +278,14 @@ void DivPlatformGenesisExt::tick() {
}
}
if (writeNoteOn) {
writes.emplace(0x28,writeMask);
immWrite(0x28,writeMask);
}
}
void DivPlatformGenesisExt::forceIns() {
DivPlatformGenesis::forceIns();
for (int i=0; i<4; i++) {
opChan[i].insChanged=true;
}
}
@ -291,7 +298,7 @@ void DivPlatformGenesisExt::reset() {
}
// channel 3 mode
writes.emplace(0x27,0x40);
immWrite(0x27,0x40);
extMode=true;
}

View File

@ -18,6 +18,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
public:
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);

View File

@ -23,4 +23,5 @@ static int orderedOps[4]={
0,2,1,3
};
#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v);}

View File

@ -6,6 +6,8 @@
#define FREQ_BASE 3424.0f
#define FREQ_BASE_PAL 3180.0f
#define rWrite(a,v) if (!skipRegisterWrites) {apu_wr_reg(a,v);}
void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
if (dacSample!=-1) {
@ -14,9 +16,9 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len)
DivSample* s=parent->song.sample[dacSample];
if (!isMuted[4]) {
if (s->depth==8) {
apu_wr_reg(0x4011,((unsigned char)s->rendData[dacPos++]+0x80)>>1);
rWrite(0x4011,((unsigned char)s->rendData[dacPos++]+0x80)>>1);
} else {
apu_wr_reg(0x4011,((unsigned short)s->rendData[dacPos++]+0x8000)>>9);
rWrite(0x4011,((unsigned short)s->rendData[dacPos++]+0x8000)>>9);
}
}
if (dacPos>=s->rendLength) {
@ -72,10 +74,10 @@ void DivPlatformNES::tick() {
chan[i].outVol=chan[i].std.vol-(15-chan[i].vol);
if (chan[i].outVol<0) chan[i].outVol=0;
if (i==2) { // triangle
apu_wr_reg(0x4000+i*4,(chan[i].outVol==0)?0:255);
rWrite(0x4000+i*4,(chan[i].outVol==0)?0:255);
chan[i].freqChanged=true;
} else {
apu_wr_reg(0x4000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6));
rWrite(0x4000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6));
}
}
if (chan[i].std.hadArp) {
@ -107,7 +109,7 @@ void DivPlatformNES::tick() {
chan[i].duty=chan[i].std.duty;
if (i==3 && chan[i].duty>1) chan[i].duty=1;
if (i!=2) {
apu_wr_reg(0x4000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6));
rWrite(0x4000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6));
}
if (i==3) { // noise
chan[i].freqChanged=true;
@ -133,18 +135,18 @@ void DivPlatformNES::tick() {
if (chan[i].keyOff) {
//rWrite(16+i*5+2,8);
if (i==2) { // triangle
apu_wr_reg(0x4000+i*4,0x00);
rWrite(0x4000+i*4,0x00);
} else {
apu_wr_reg(0x4000+i*4,0x30);
rWrite(0x4000+i*4,0x30);
}
}
if (i==3) { // noise
apu_wr_reg(0x4002+i*4,(chan[i].duty<<7)|chan[i].freq);
apu_wr_reg(0x4003+i*4,0xf0);
rWrite(0x4002+i*4,(chan[i].duty<<7)|chan[i].freq);
rWrite(0x4003+i*4,0xf0);
} else {
apu_wr_reg(0x4002+i*4,chan[i].freq&0xff);
rWrite(0x4002+i*4,chan[i].freq&0xff);
if ((chan[i].prevFreq>>8)!=(chan[i].freq>>8) || i==2) {
apu_wr_reg(0x4003+i*4,0xf8|(chan[i].freq>>8));
rWrite(0x4003+i*4,0xf8|(chan[i].freq>>8));
}
if (chan[i].freq!=65535 && chan[i].freq!=0) {
chan[i].prevFreq=chan[i].freq;
@ -181,9 +183,9 @@ int DivPlatformNES::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (c.chan==2) {
apu_wr_reg(0x4000+c.chan*4,0xff);
rWrite(0x4000+c.chan*4,0xff);
} else {
apu_wr_reg(0x4000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
rWrite(0x4000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
}
break;
case DIV_CMD_NOTE_OFF:
@ -204,9 +206,9 @@ int DivPlatformNES::dispatch(DivCommand c) {
chan[c.chan].outVol=c.value;
}
if (c.chan==2) {
apu_wr_reg(0x4000+c.chan*4,0xff);
rWrite(0x4000+c.chan*4,0xff);
} else {
apu_wr_reg(0x4000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
rWrite(0x4000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
}
}
break;
@ -276,9 +278,16 @@ int DivPlatformNES::dispatch(DivCommand c) {
void DivPlatformNES::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
apu_wr_reg(0x4015,(!isMuted[0])|((!isMuted[1])<<1)|((!isMuted[2])<<2)|((!isMuted[3])<<3)|((!isMuted[4])<<4));
rWrite(0x4015,(!isMuted[0])|((!isMuted[1])<<1)|((!isMuted[2])<<2)|((!isMuted[3])<<3)|((!isMuted[4])<<4));
if (isMuted[4]) {
apu_wr_reg(0x4011,0);
rWrite(0x4011,0);
}
}
void DivPlatformNES::forceIns() {
for (int i=0; i<5; i++) {
chan[i].insChanged=true;
chan[i].prevFreq=65535;
}
}
@ -297,9 +306,9 @@ void DivPlatformNES::reset() {
apu.cpu_cycles=0;
apu.cpu_opcode_cycle=0;
apu_wr_reg(0x4015,(!isMuted[0])|((!isMuted[1])<<1)|((!isMuted[2])<<2)|((!isMuted[3])<<3)|((!isMuted[4])<<4));
apu_wr_reg(0x4001,0x08);
apu_wr_reg(0x4005,0x08);
rWrite(0x4015,(!isMuted[0])|((!isMuted[1])<<1)|((!isMuted[2])<<2)|((!isMuted[3])<<3)|((!isMuted[4])<<4));
rWrite(0x4001,0x08);
rWrite(0x4005,0x08);
}
bool DivPlatformNES::keyOffAffectsArp(int ch) {

View File

@ -41,11 +41,11 @@ class DivPlatformNES: public DivDispatch {
float freqBase;
void updateWave();
public:
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);

View File

@ -3,13 +3,15 @@
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) writes.emplace(a,v);
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v);}
#define chWrite(c,a,v) \
if (curChan!=c) { \
curChan=c; \
rWrite(0,curChan); \
} \
rWrite(a,v);
if (!skipRegisterWrites) { \
if (curChan!=c) { \
curChan=c; \
rWrite(0,curChan); \
} \
rWrite(a,v); \
}
#define FREQ_BASE 1712.0f*2
@ -268,6 +270,13 @@ void DivPlatformPCE::muteChannel(int ch, bool mute) {
chWrite(ch,0x05,isMuted[ch]?0:chan[ch].pan);
}
void DivPlatformPCE::forceIns() {
for (int i=0; i<6; i++) {
chan[i].insChanged=true;
updateWave(i);
}
}
void DivPlatformPCE::reset() {
while (!writes.empty()) writes.pop();
for (int i=0; i<6; i++) {

View File

@ -57,6 +57,7 @@ class DivPlatformPCE: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
bool isStereo();

View File

@ -104,14 +104,14 @@ void DivPlatformYM2610::tick() {
for (int i=0; i<4; i++) {
if (i==1 && extMode) continue;
if (chan[i].keyOn || chan[i].keyOff) {
writes.emplace(0x28,0x00|konOffs[i]);
immWrite(0x28,0x00|konOffs[i]);
chan[i].keyOff=false;
}
}
for (int i=0; i<512; i++) {
if (pendingWrites[i]!=oldWrites[i]) {
writes.emplace(i,pendingWrites[i]&0xff);
immWrite(i,pendingWrites[i]&0xff);
oldWrites[i]=pendingWrites[i];
}
}
@ -121,12 +121,12 @@ void DivPlatformYM2610::tick() {
if (chan[i].freqChanged) {
chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE+chan[i].pitch))/ONE_SEMITONE;
int freqt=toFreq(chan[i].freq);
writes.emplace(chanOffs[i]+0xa4,freqt>>8);
writes.emplace(chanOffs[i]+0xa0,freqt&0xff);
immWrite(chanOffs[i]+0xa4,freqt>>8);
immWrite(chanOffs[i]+0xa0,freqt&0xff);
chan[i].freqChanged=false;
}
if (chan[i].keyOn) {
writes.emplace(0x28,0xf0|konOffs[i]);
immWrite(0x28,0xf0|konOffs[i]);
chan[i].keyOn=false;
}
}
@ -177,22 +177,23 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
if (c.chan>6) { // ADPCM
if (skipRegisterWrites) break;
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
writes.emplace(0x100,0x80|(1<<(c.chan-7)));
writes.emplace(0x110+c.chan-7,0);
writes.emplace(0x118+c.chan-7,0);
writes.emplace(0x120+c.chan-7,0);
writes.emplace(0x128+c.chan-7,0);
immWrite(0x100,0x80|(1<<(c.chan-7)));
immWrite(0x110+c.chan-7,0);
immWrite(0x118+c.chan-7,0);
immWrite(0x120+c.chan-7,0);
immWrite(0x128+c.chan-7,0);
break;
}
DivSample* s=parent->song.sample[12*sampleBank+c.value%12];
writes.emplace(0x110+c.chan-7,(s->rendOff>>8)&0xff);
writes.emplace(0x118+c.chan-7,s->rendOff>>16);
immWrite(0x110+c.chan-7,(s->rendOff>>8)&0xff);
immWrite(0x118+c.chan-7,s->rendOff>>16);
int end=s->rendOff+s->adpcmRendLength-1;
writes.emplace(0x120+c.chan-7,(end>>8)&0xff);
writes.emplace(0x128+c.chan-7,end>>16);
writes.emplace(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
writes.emplace(0x100,0x00|(1<<(c.chan-7)));
immWrite(0x120+c.chan-7,(end>>8)&0xff);
immWrite(0x128+c.chan-7,end>>16);
immWrite(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
immWrite(0x100,0x00|(1<<(c.chan-7)));
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
@ -247,7 +248,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
}
case DIV_CMD_NOTE_OFF:
if (c.chan>6) {
writes.emplace(0x100,0x80|(1<<(c.chan-7)));
immWrite(0x100,0x80|(1<<(c.chan-7)));
break;
}
chan[c.chan].keyOff=true;
@ -258,7 +259,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].vol=c.value;
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
if (c.chan>6) { // ADPCM
writes.emplace(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
immWrite(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
break;
}
if (c.chan>3) { // PSG
@ -306,7 +307,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
break;
}
if (c.chan>6) {
writes.emplace(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
immWrite(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
break;
}
if (c.chan>3) break;
@ -434,7 +435,8 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
break;
case DIV_CMD_AY_ENVELOPE_SET:
if (c.chan<4 || c.chan>6) break;
rWrite(0x0d,c.value>>4);
ayEnvMode=c.value>>4;
rWrite(0x0d,ayEnvMode);
if (c.value&15) {
chan[c.chan].psgMode|=4;
} else {
@ -450,15 +452,15 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
if (c.chan<4 || c.chan>6) break;
ayEnvPeriod&=0xff00;
ayEnvPeriod|=c.value;
writes.emplace(0x0b,ayEnvPeriod);
writes.emplace(0x0c,ayEnvPeriod>>8);
immWrite(0x0b,ayEnvPeriod);
immWrite(0x0c,ayEnvPeriod>>8);
break;
case DIV_CMD_AY_ENVELOPE_HIGH:
if (c.chan<4 || c.chan>6) break;
ayEnvPeriod&=0xff;
ayEnvPeriod|=c.value<<8;
writes.emplace(0x0b,ayEnvPeriod);
writes.emplace(0x0c,ayEnvPeriod>>8);
immWrite(0x0b,ayEnvPeriod);
immWrite(0x0c,ayEnvPeriod>>8);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
@ -486,7 +488,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
void DivPlatformYM2610::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>6) { // ADPCM
writes.emplace(0x108+(ch-7),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].vol));
immWrite(0x108+(ch-7),isMuted[ch]?0:((chan[ch].pan<<6)|chan[ch].vol));
return;
}
if (ch>3) { // PSG
@ -502,6 +504,15 @@ void DivPlatformYM2610::muteChannel(int ch, bool mute) {
rWrite(chanOffs[ch]+0xb4,(isMuted[ch]?0:(chan[ch].pan<<6))|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
}
void DivPlatformYM2610::forceIns() {
for (int i=0; i<13; i++) {
chan[i].insChanged=true;
}
immWrite(0x0b,ayEnvPeriod);
immWrite(0x0c,ayEnvPeriod>>8);
immWrite(0x0d,ayEnvMode);
}
void DivPlatformYM2610::reset() {
while (!writes.empty()) writes.pop();
fm->reset();
@ -531,16 +542,17 @@ void DivPlatformYM2610::reset() {
dacSample=-1;
sampleBank=0;
ayEnvPeriod=0;
ayEnvMode=0;
delay=0;
extMode=false;
// LFO
writes.emplace(0x22,0x08);
immWrite(0x22,0x08);
// PCM volume
writes.emplace(0x101,0x3f);
immWrite(0x101,0x3f);
}
bool DivPlatformYM2610::isStereo() {

View File

@ -54,6 +54,7 @@ class DivPlatformYM2610: public DivDispatch {
short oldWrites[512];
short pendingWrites[512];
unsigned char ayEnvMode;
unsigned short ayEnvPeriod;
int octave(int freq);
@ -63,6 +64,7 @@ class DivPlatformYM2610: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
bool isStereo();

View File

@ -207,7 +207,7 @@ void DivPlatformYM2610Ext::tick() {
}
}
if (writeSomething) {
writes.emplace(0x28,writeMask);
immWrite(0x28,writeMask);
}
}
@ -221,8 +221,8 @@ void DivPlatformYM2610Ext::tick() {
int freqt=toFreq(opChan[i].freq);
opChan[i].freqH=freqt>>8;
opChan[i].freqL=freqt&0xff;
writes.emplace(opChanOffsH[i],opChan[i].freqH);
writes.emplace(opChanOffsL[i],opChan[i].freqL);
immWrite(opChanOffsH[i],opChan[i].freqH);
immWrite(opChanOffsL[i],opChan[i].freqL);
opChan[i].freqChanged=false;
}
writeMask|=opChan[i].active<<(4+i);
@ -233,7 +233,7 @@ void DivPlatformYM2610Ext::tick() {
}
}
if (writeNoteOn) {
writes.emplace(0x28,writeMask);
immWrite(0x28,writeMask);
}
}
@ -261,6 +261,13 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
}
}
void DivPlatformYM2610Ext::forceIns() {
DivPlatformYM2610::forceIns();
for (int i=0; i<4; i++) {
opChan[i].insChanged=true;
}
}
void DivPlatformYM2610Ext::reset() {
DivPlatformYM2610::reset();
@ -270,7 +277,7 @@ void DivPlatformYM2610Ext::reset() {
}
// channel 2 mode
writes.emplace(0x27,0x40);
immWrite(0x27,0x40);
extMode=true;
}

View File

@ -18,6 +18,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
public:
int dispatch(DivCommand c);
void reset();
void forceIns();
void tick();
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);

View File

@ -23,7 +23,8 @@ static int orderedOps[4]={
0,2,1,3
};
#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v);}
#define FM_FREQ_BASE 622.0f
#define PSG_FREQ_BASE 7640.0f