NES: wire up NSFplay

no muting supported for now
see #27
This commit is contained in:
tildearrow 2022-05-01 23:20:20 -05:00
parent 7b31f6a3e5
commit cb7aa4aa05
11 changed files with 193 additions and 99 deletions

View File

@ -189,6 +189,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
break;
case DIV_SYSTEM_NES:
dispatch=new DivPlatformNES;
((DivPlatformNES*)dispatch)->setNSFPlay(eng->getConfInt("nesCore",0)==1);
break;
case DIV_SYSTEM_C64_6581:
dispatch=new DivPlatformC64;

View File

@ -27,7 +27,7 @@ struct _nla_table nla_table;
#define CHIP_DIVIDER 16
#define rWrite(a,v) if (!skipRegisterWrites) {apu_wr_reg(nes,a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {doWrite(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
const char* regCheatSheetNES[]={
"S0Volume", "4000",
@ -75,36 +75,48 @@ const char* DivPlatformNES::getEffectName(unsigned char effect) {
return NULL;
}
void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) {
void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
if (useNP) {
nes1_NP->Write(addr,data);
nes2_NP->Write(addr,data);
} else {
apu_wr_reg(nes,addr,data);
}
}
#define doPCM \
if (dacSample!=-1) { \
dacPeriod+=dacRate; \
if (dacPeriod>=rate) { \
DivSample* s=parent->getSample(dacSample); \
if (s->samples>0) { \
if (!isMuted[4]) { \
unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1; \
if (dacAntiClickOn && dacAntiClick<next) { \
dacAntiClick+=8; \
rWrite(0x4011,dacAntiClick); \
} else { \
dacAntiClickOn=false; \
rWrite(0x4011,next); \
} \
} \
if (++dacPos>=s->samples) { \
if (s->loopStart>=0 && s->loopStart<(int)s->samples) { \
dacPos=s->loopStart; \
} else { \
dacSample=-1; \
} \
} \
dacPeriod-=rate; \
} else { \
dacSample=-1; \
} \
} \
}
void DivPlatformNES::acquire_puNES(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
if (dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[4]) {
unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1;
if (dacAntiClickOn && dacAntiClick<next) {
dacAntiClick+=8;
rWrite(0x4011,dacAntiClick);
} else {
dacAntiClickOn=false;
rWrite(0x4011,next);
}
}
if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
dacPos=s->loopStart;
} else {
dacSample=-1;
}
}
dacPeriod-=rate;
} else {
dacSample=-1;
}
}
}
doPCM;
apu_tick(nes,NULL);
nes->apu.odd_cycle=!nes->apu.odd_cycle;
@ -126,6 +138,32 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len)
}
}
void DivPlatformNES::acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len) {
int out1[2];
int out2[2];
for (size_t i=start; i<start+len; i++) {
doPCM;
nes1_NP->Tick(1);
nes2_NP->Tick(1);
nes1_NP->Render(out1);
nes2_NP->Render(out2);
int sample=out1[0]+out1[1]+out2[0]+out2[1];
if (sample>32767) sample=32767;
if (sample<-32768) sample=-32768;
bufL[i]=sample;
}
}
void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) {
if (useNP) {
acquire_NSFPlay(bufL,bufR,start,len);
} else {
acquire_puNES(bufL,bufR,start,len);
}
}
static unsigned char noiseTable[253]={
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4,
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
@ -466,7 +504,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
void DivPlatformNES::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
nes->muted[ch]=mute;
if (!useNP) nes->muted[ch]=mute;
}
void DivPlatformNES::forceIns() {
@ -513,10 +551,15 @@ void DivPlatformNES::reset() {
dacSample=-1;
sampleBank=0;
apu_turn_on(nes,apuType);
if (useNP) {
nes1_NP->Reset();
nes2_NP->Reset();
} else {
apu_turn_on(nes,apuType);
nes->apu.cpu_cycles=0;
nes->apu.cpu_opcode_cycle=0;
}
memset(regPool,0,128);
nes->apu.cpu_cycles=0;
nes->apu.cpu_opcode_cycle=0;
rWrite(0x4015,0x1f);
rWrite(0x4001,chan[0].sweep);
@ -534,14 +577,20 @@ void DivPlatformNES::setFlags(unsigned int flags) {
if (flags==2) { // Dendy
rate=COLOR_PAL*2.0/5.0;
apuType=2;
nes->apu.type=apuType;
} else if (flags==1) { // PAL
rate=COLOR_PAL*3.0/8.0;
apuType=1;
nes->apu.type=apuType;
} else { // NTSC
rate=COLOR_NTSC/2.0;
apuType=0;
}
if (useNP) {
nes1_NP->SetClock(rate);
nes1_NP->SetRate(rate);
nes2_NP->SetClock(rate);
nes2_NP->SetRate(rate);
nes2_NP->SetPal(apuType==1);
} else {
nes->apu.type=apuType;
}
chipClock=rate;
@ -564,16 +613,31 @@ void DivPlatformNES::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
}
void DivPlatformNES::setNSFPlay(bool use) {
useNP=use;
}
int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
parent=p;
apuType=flags;
dumpWrites=false;
skipRegisterWrites=false;
nes=new struct NESAPU;
if (useNP) {
nes1_NP=new xgm::NES_APU;
nes1_NP->SetOption(xgm::NES_APU::OPT_NONLINEAR_MIXER,1);
nes2_NP=new xgm::NES_DMC;
nes2_NP->SetOption(xgm::NES_DMC::OPT_NONLINEAR_MIXER,1);
nes2_NP->SetMemory([](unsigned short addr, unsigned int& data) {
data=0;
});
nes2_NP->SetAPU(nes1_NP);
} else {
nes=new struct NESAPU;
}
writeOscBuf=0;
for (int i=0; i<5; i++) {
isMuted[i]=false;
nes->muted[i]=false;
if (!useNP) nes->muted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
setFlags(flags);
@ -587,7 +651,12 @@ void DivPlatformNES::quit() {
for (int i=0; i<5; i++) {
delete oscBuf[i];
}
delete nes;
if (useNP) {
delete nes1_NP;
delete nes2_NP;
} else {
delete nes;
}
}
DivPlatformNES::~DivPlatformNES() {

View File

@ -23,6 +23,8 @@
#include "../dispatch.h"
#include "../macroInt.h"
#include "sound/nes_nsfplay/nes_apu.h"
class DivPlatformNES: public DivDispatch {
struct Channel {
int freq, baseFreq, pitch, pitch2, prevFreq, note, ins;
@ -66,11 +68,18 @@ class DivPlatformNES: public DivDispatch {
unsigned char writeOscBuf;
unsigned char apuType;
bool dacAntiClickOn;
bool useNP;
struct NESAPU* nes;
xgm::NES_APU* nes1_NP;
xgm::NES_DMC* nes2_NP;
unsigned char regPool[128];
friend void putDispatchChan(void*,int,int);
void doWrite(unsigned short addr, unsigned char data);
void acquire_puNES(short* bufL, short* bufR, size_t start, size_t len);
void acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len);
public:
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
@ -84,6 +93,7 @@ class DivPlatformNES: public DivDispatch {
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
float getPostAmp();
void setNSFPlay(bool use);
void setFlags(unsigned int flags);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);

View File

@ -68,16 +68,16 @@ namespace xgm
void FrameSequence(int s);
virtual void Reset ();
virtual void Tick (unsigned int clocks);
virtual unsigned int Render (int b[2]);
virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual void SetRate (double rate);
virtual void SetClock (double clock);
virtual void SetOption (int id, int b);
virtual void SetMask(int m){ mask = m; }
virtual void SetStereoMix (int trk, short mixl, short mixr);
void Reset ();
void Tick (unsigned int clocks);
unsigned int Render (int b[2]);
bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
void SetRate (double rate);
void SetClock (double clock);
void SetOption (int id, int b);
void SetMask(int m){ mask = m; }
void SetStereoMix (int trk, short mixl, short mixr);
};
} // namespace

View File

@ -102,16 +102,16 @@ namespace xgm
int GetDamp(){ return (damp<<1)|dac_lsb ; }
void TickFrameSequence (unsigned int clocks);
virtual void Reset ();
virtual void Tick (unsigned int clocks);
virtual unsigned int Render (int b[2]);
virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual void SetRate (double rate);
virtual void SetClock (double rate);
virtual void SetOption (int, int);
virtual void SetMask(int m){ mask = m; }
virtual void SetStereoMix (int trk, short mixl, short mixr);
void Reset ();
void Tick (unsigned int clocks);
unsigned int Render (int b[2]);
bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
void SetRate (double rate);
void SetClock (double rate);
void SetOption (int, int);
void SetMask(int m){ mask = m; }
void SetStereoMix (int trk, short mixl, short mixr);
};
}

View File

@ -56,16 +56,16 @@ public:
NES_FDS ();
~NES_FDS ();
virtual void Reset ();
virtual void Tick (unsigned int clocks);
virtual unsigned int Render (int b[2]);
virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual void SetRate (double);
virtual void SetClock (double);
virtual void SetOption (int, int);
virtual void SetMask(int m){ mask = m&1; }
virtual void SetStereoMix (int trk, short mixl, short mixr);
void Reset ();
void Tick (unsigned int clocks);
unsigned int Render (int b[2]);
bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
void SetRate (double);
void SetClock (double);
void SetOption (int, int);
void SetMask(int m){ mask = m&1; }
void SetStereoMix (int trk, short mixl, short mixr);
};
} // namespace xgm

View File

@ -50,16 +50,16 @@ namespace xgm
void FrameSequence ();
void TickFrameSequence (unsigned int clocks);
virtual void Reset ();
virtual void Tick (unsigned int clocks);
virtual unsigned int Render (int b[2]);
virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual void SetOption (int id, int b);
virtual void SetClock (double);
virtual void SetRate (double);
virtual void SetMask (int m){ mask = m; }
virtual void SetStereoMix (int trk, short mixl, short mixr);
void Reset ();
void Tick (unsigned int clocks);
unsigned int Render (int b[2]);
bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
void SetOption (int id, int b);
void SetClock (double);
void SetRate (double);
void SetMask (int m){ mask = m; }
void SetStereoMix (int trk, short mixl, short mixr);
};
}

View File

@ -47,16 +47,16 @@ public:
NES_N106 ();
~NES_N106 ();
virtual void Reset ();
virtual void Tick (unsigned int clocks);
virtual unsigned int Render (int b[2]);
virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual void SetRate (double);
virtual void SetClock (double);
virtual void SetOption (int, int);
virtual void SetMask (int m);
virtual void SetStereoMix (int trk, short mixl, short mixr);
void Reset ();
void Tick (unsigned int clocks);
unsigned int Render (int b[2]);
bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
void SetRate (double);
void SetClock (double);
void SetOption (int, int);
void SetMask (int m);
void SetStereoMix (int trk, short mixl, short mixr);
};
} // namespace xgm

View File

@ -36,16 +36,16 @@ namespace xgm
NES_VRC6 ();
~NES_VRC6 ();
virtual void Reset ();
virtual void Tick (unsigned int clocks);
virtual unsigned int Render (int b[2]);
virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual void SetClock (double);
virtual void SetRate (double);
virtual void SetOption (int, int);
virtual void SetMask (int m){ mask = m; }
virtual void SetStereoMix (int trk, short mixl, short mixr);
void Reset ();
void Tick (unsigned int clocks);
unsigned int Render (int b[2]);
bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
void SetClock (double);
void SetRate (double);
void SetOption (int, int);
void SetMask (int m){ mask = m; }
void SetStereoMix (int trk, short mixl, short mixr);
};
} // namespace

View File

@ -788,6 +788,7 @@ class FurnaceGUI {
int arcadeCore;
int ym2612Core;
int saaCore;
int nesCore;
int mainFont;
int patFont;
int audioRate;
@ -870,6 +871,7 @@ class FurnaceGUI {
arcadeCore(0),
ym2612Core(0),
saaCore(1),
nesCore(0),
mainFont(0),
patFont(0),
audioRate(44100),

View File

@ -84,6 +84,11 @@ const char* saaCores[]={
"SAASound"
};
const char* nesCores[]={
"puNES",
"NSFplay"
};
const char* valueInputStyles[]={
"Disabled/custom",
"Two octaves (0 is C-4, F is D#5)",
@ -859,6 +864,10 @@ void FurnaceGUI::drawSettings() {
ImGui::SameLine();
ImGui::Combo("##SAACore",&settings.saaCore,saaCores,2);
ImGui::Text("NES core");
ImGui::SameLine();
ImGui::Combo("##NESCore",&settings.nesCore,nesCores,2);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Appearance")) {
@ -1731,6 +1740,7 @@ void FurnaceGUI::syncSettings() {
settings.arcadeCore=e->getConfInt("arcadeCore",0);
settings.ym2612Core=e->getConfInt("ym2612Core",0);
settings.saaCore=e->getConfInt("saaCore",1);
settings.nesCore=e->getConfInt("nesCore",0);
settings.mainFont=e->getConfInt("mainFont",0);
settings.patFont=e->getConfInt("patFont",0);
settings.mainFontPath=e->getConfString("mainFontPath","");
@ -1805,6 +1815,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.arcadeCore,0,1);
clampSetting(settings.ym2612Core,0,1);
clampSetting(settings.saaCore,0,1);
clampSetting(settings.nesCore,0,1);
clampSetting(settings.mainFont,0,6);
clampSetting(settings.patFont,0,6);
clampSetting(settings.patRowsBase,0,1);
@ -1907,6 +1918,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("arcadeCore",settings.arcadeCore);
e->setConf("ym2612Core",settings.ym2612Core);
e->setConf("saaCore",settings.saaCore);
e->setConf("nesCore",settings.nesCore);
e->setConf("mainFont",settings.mainFont);
e->setConf("patFont",settings.patFont);
e->setConf("mainFontPath",settings.mainFontPath);