From a6ef36d42e3c4940bc35562275cff5f441224ec7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 14:51:06 -0500 Subject: [PATCH 1/5] SoundUnit: PCM support --- src/engine/platform/su.cpp | 84 ++++++++++++++++++++++++++++++++++++-- src/engine/platform/su.h | 5 +++ src/engine/sample.h | 3 +- src/engine/song.h | 2 +- src/gui/debugWindow.cpp | 3 +- 5 files changed, 90 insertions(+), 7 deletions(-) diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index a75b7c0b..284c2080 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -19,6 +19,7 @@ #include "su.h" #include "../engine.h" +#include "../../ta-log.h" #include //#define rWrite(a,v) pendingWrites[a]=v; @@ -178,12 +179,47 @@ void DivPlatformSoundUnit::tick(bool sysTick) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); + if (chan[i].pcm) { + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + DivSample* sample=parent->getSample(ins->amiga.initSample); + if (sample!=NULL) { + double off=1.0; + if (sample->centerRate<1) { + off=1.0; + } else { + off=(double)sample->centerRate/8363.0; + } + chan[i].freq=(double)chan[i].freq*off; + } + } + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>65535) chan[i].freq=65535; chWrite(i,0x00,chan[i].freq&0xff); chWrite(i,0x01,chan[i].freq>>8); - if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { - //rWrite(16+i*5,0x80); - //chWrite(i,0x04,0x80|chan[i].vol); + if (chan[i].pcm) { + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + DivSample* sample=parent->getSample(ins->amiga.initSample); + if (sample!=NULL) { + unsigned int sampleEnd=sample->offSU+sample->samples; + if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1; + chWrite(i,0x0a,sample->offSU&0xff); + chWrite(i,0x0b,sample->offSU>>8); + chWrite(i,0x0c,sampleEnd&0xff); + chWrite(i,0x0d,sampleEnd>>8); + if (sample->loopStart>=0 && sample->loopStart<(int)sample->samples) { + unsigned int sampleLoop=sample->offSU+sample->loopStart; + if (sampleLoop>=getSampleMemCapacity(0)) sampleLoop=getSampleMemCapacity(0)-1; + chWrite(i,0x0e,sampleLoop&0xff); + chWrite(i,0x0f,sampleLoop>>8); + chan[i].pcmLoop=true; + } else { + chan[i].pcmLoop=false; + } + writeControl(i); + writeControlUpper(i); + } + } } if (chan[i].keyOff) { chWrite(i,0x02,0); @@ -199,6 +235,11 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SU); + if (chan[c.chan].pcm && ins->type!=DIV_INS_AMIGA) { + writeControl(c.chan); + writeControlUpper(c.chan); + } + chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; @@ -247,7 +288,6 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { break; case DIV_CMD_WAVE: chan[c.chan].wave=c.value; - chan[c.chan].keyOn=true; break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_FREQUENCY(c.value2); @@ -385,6 +425,42 @@ void DivPlatformSoundUnit::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); } +const void* DivPlatformSoundUnit::getSampleMem(int index) { + return (index==0)?su->pcm:NULL; +} + +size_t DivPlatformSoundUnit::getSampleMemCapacity(int index) { + return (index==0)?8192:0; +} + +size_t DivPlatformSoundUnit::getSampleMemUsage(int index) { + return (index==0)?sampleMemLen:0; +} + +void DivPlatformSoundUnit::renderSamples() { + memset(su->pcm,0,getSampleMemCapacity(0)); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=s->samples; + if (memPos>=getSampleMemCapacity(0)) { + logW("out of PCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(su->pcm+memPos,s->data8,getSampleMemCapacity(0)-memPos); + logW("out of PCM memory for sample %d!",i); + } else { + memcpy(su->pcm+memPos,s->data8,paddedLen); + } + s->offSU=memPos; + memPos+=paddedLen; + } + sampleMemLen=memPos; + +} + int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 7ff3e870..22ea1e78 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -86,6 +86,7 @@ class DivPlatformSoundUnit: public DivDispatch { short tempR; unsigned char sampleBank, lfoMode, lfoSpeed; SoundUnit* su; + size_t sampleMemLen; unsigned char regPool[128]; void writeControl(int ch); void writeControlUpper(int ch); @@ -110,6 +111,10 @@ class DivPlatformSoundUnit: public DivDispatch { void poke(std::vector& wlist); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); + const void* getSampleMem(int index); + size_t getSampleMemCapacity(int index); + size_t getSampleMemUsage(int index); + void renderSamples(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); ~DivPlatformSoundUnit(); diff --git a/src/engine/sample.h b/src/engine/sample.h index ae0c06da..6e4de094 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -86,7 +86,7 @@ struct DivSample { unsigned int length8, length16, length1, lengthDPCM, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX; unsigned int off8, off16, off1, offDPCM, offQSoundA, offA, offB, offX68, offBRR, offVOX; - unsigned int offSegaPCM, offQSound, offX1_010; + unsigned int offSegaPCM, offQSound, offX1_010, offSU; unsigned int samples; @@ -247,6 +247,7 @@ struct DivSample { offSegaPCM(0), offQSound(0), offX1_010(0), + offSU(0), samples(0) {} ~DivSample(); }; diff --git a/src/engine/song.h b/src/engine/song.h index 8e67cce8..c0852ea0 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -457,7 +457,7 @@ struct DivSong { system[0]=DIV_SYSTEM_YM2612; system[1]=DIV_SYSTEM_SMS; - nullInsOPLL.fm.opllPreset=7; + nullInsOPLL.fm.opllPreset=0; nullInsOPLL.fm.op[1].tl=0; nullInsOPLL.name="This is a bug! Report!"; diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 7b232a9c..566f916f 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -180,6 +180,7 @@ void FurnaceGUI::drawDebug() { ImGui::Text("offSegaPCM: %x",sample->offSegaPCM); ImGui::Text("offQSound: %x",sample->offQSound); ImGui::Text("offX1_010: %x",sample->offX1_010); + ImGui::Text("offSU: %x",sample->offSU); ImGui::Text("samples: %d",sample->samples); ImGui::Unindent(); @@ -312,4 +313,4 @@ void FurnaceGUI::drawDebug() { } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_DEBUG; ImGui::End(); -} \ No newline at end of file +} From ce40085d3b77233bf655e8a24ce7b66cc95509b7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 15:07:59 -0500 Subject: [PATCH 2/5] SoundUnit: fix PCM pitch --- src/engine/platform/su.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 284c2080..e80153f0 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -120,7 +120,12 @@ void DivPlatformSoundUnit::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7; + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + if (ins->type==DIV_INS_AMIGA) { + chan[i].outVol=((chan[i].vol&127)*MIN(64,chan[i].std.vol.val))>>6; + } else { + chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7; + } chWrite(i,0x02,chan[i].outVol); } if (chan[i].std.arp.had) { @@ -183,11 +188,11 @@ void DivPlatformSoundUnit::tick(bool sysTick) { DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivSample* sample=parent->getSample(ins->amiga.initSample); if (sample!=NULL) { - double off=1.0; + double off=0.25; if (sample->centerRate<1) { - off=1.0; + off=0.25; } else { - off=(double)sample->centerRate/8363.0; + off=(double)sample->centerRate/(8363.0*4.0); } chan[i].freq=(double)chan[i].freq*off; } From 960048cf4b5924076a9473dffe1e0c9121610790 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 16:05:07 -0500 Subject: [PATCH 3/5] NES: fix Furnace-style DPCM --- src/engine/platform/nes.cpp | 48 +++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 0e336df0..dffcb7ee 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -313,8 +313,6 @@ void DivPlatformNES::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; } if (chan[i].keyOn) { - //rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); - //rWrite(16+i*5+2,((chan[i].vol<<4))|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3)); } if (chan[i].keyOff) { //rWrite(16+i*5+2,8); @@ -343,7 +341,7 @@ void DivPlatformNES::tick(bool sysTick) { } // PCM - if (chan[4].freqChanged) { + if (chan[4].freqChanged || chan[4].keyOn) { chan[4].freq=parent->calcFreq(chan[4].baseFreq,chan[4].pitch,false); if (chan[4].furnaceDac) { double off=1.0; @@ -352,11 +350,27 @@ void DivPlatformNES::tick(bool sysTick) { off=(double)s->centerRate/8363.0; } dacRate=MIN(chan[4].freq*off,32000); - if (dpcmMode && !skipRegisterWrites) { - rWrite(0x4010,calcDPCMRate(dacRate)); + if (chan[4].keyOn) { + if (dpcmMode && !skipRegisterWrites && dacSample>=0 && dacSamplesong.sampleLen) { + unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM; + unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; + if (dpcmLen>255) dpcmLen=255; + // write DPCM + rWrite(0x4015,15); + rWrite(0x4010,calcDPCMRate(dacRate)); + rWrite(0x4012,(dpcmAddr>>6)&0xff); + rWrite(0x4013,dpcmLen&0xff); + rWrite(0x4015,31); + dpcmBank=dpcmAddr>>14; + } + } else { + if (dpcmMode) { + rWrite(0x4010,calcDPCMRate(dacRate)); + } } - if (dumpWrites) addWrite(0xffff0001,dacRate); + if (dumpWrites && !dpcmMode) addWrite(0xffff0001,dacRate); } + if (chan[4].keyOn) chan[4].keyOn=false; chan[4].freqChanged=false; } } @@ -378,25 +392,13 @@ int DivPlatformNES::dispatch(DivCommand c) { dacPos=0; dacPeriod=0; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=parent->song.tuning*pow(2.0f,((float)(c.value+3)/12.0f)); + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } chan[c.chan].active=true; chan[c.chan].keyOn=true; chan[c.chan].furnaceDac=true; - if (dpcmMode && !skipRegisterWrites) { - unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM; - unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; - if (dpcmLen>255) dpcmLen=255; - // write DPCM - rWrite(0x4015,15); - rWrite(0x4010,calcDPCMRate(chan[c.chan].baseFreq)); - rWrite(0x4012,(dpcmAddr>>6)&0xff); - rWrite(0x4013,dpcmLen&0xff); - rWrite(0x4015,31); - dpcmBank=dpcmAddr>>14; - } } else { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; @@ -492,7 +494,7 @@ int DivPlatformNES::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=(c.chan==4)?(parent->calcBaseFreq(1,1,c.value2,false)):(NOTE_PERIODIC(c.value2)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -554,7 +556,11 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: if (c.chan==3) break; - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + if (c.chan==4) { + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)),false); + } else { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + } chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; From 62289d924eb5ed9dfcf1178cbc44edbe4b40bbdd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 16:53:55 -0500 Subject: [PATCH 4/5] FDS: add NSFPlay core --- src/engine/dispatchContainer.cpp | 1 + src/engine/platform/fds.cpp | 65 +++++++++++++++++-- src/engine/platform/fds.h | 9 +++ .../platform/sound/nes_nsfplay/nes_fds.cpp | 2 + src/gui/gui.h | 2 + src/gui/settings.cpp | 7 ++ 6 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 187523c7..f44ed0d5 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -228,6 +228,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do break; case DIV_SYSTEM_FDS: dispatch=new DivPlatformFDS; + ((DivPlatformFDS*)dispatch)->setNSFPlay(eng->getConfInt("fdsCore",0)==1); break; case DIV_SYSTEM_TIA: dispatch=new DivPlatformTIA; diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 4c8e92b3..f3908148 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -20,11 +20,12 @@ #include "fds.h" #include "sound/nes/cpu_inline.h" #include "../engine.h" +#include "sound/nes_nsfplay/nes_fds.h" #include #define CHIP_FREQBASE 262144 -#define rWrite(a,v) if (!skipRegisterWrites) {fds_wr_mem(fds,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* regCheatSheetFDS[]={ "IOCtrl", "4023", @@ -78,7 +79,7 @@ const char* DivPlatformFDS::getEffectName(unsigned char effect) { return NULL; } -void DivPlatformFDS::acquire(short* bufL, short* bufR, size_t start, size_t len) { +void DivPlatformFDS::acquire_puNES(short* bufL, short* bufR, size_t start, size_t len) { for (size_t i=start; isnd.main.output; @@ -92,6 +93,38 @@ void DivPlatformFDS::acquire(short* bufL, short* bufR, size_t start, size_t len) } } +void DivPlatformFDS::acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len) { + int out[2]; + for (size_t i=start; iTick(1); + fds_NP->Render(out); + int sample=isMuted[0]?0:(out[0]<<1); + if (sample>32767) sample=32767; + if (sample<-32768) sample=-32768; + bufL[i]=sample; + if (++writeOscBuf>=32) { + writeOscBuf=0; + oscBuf->data[oscBuf->needle++]=sample<<1; + } + } +} + +void DivPlatformFDS::doWrite(unsigned short addr, unsigned char data) { + if (useNP) { + fds_NP->Write(addr,data); + } else { + fds_wr_mem(fds,addr,data); + } +} + +void DivPlatformFDS::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); + } +} + void DivPlatformFDS::updateWave() { // TODO: master volume rWrite(0x4089,0x80); @@ -423,7 +456,11 @@ void DivPlatformFDS::reset() { addWrite(0xffffffff,0); } - fds_reset(fds); + if (useNP) { + fds_NP->Reset(); + } else { + fds_reset(fds); + } memset(regPool,0,128); rWrite(0x4023,0); @@ -435,6 +472,10 @@ bool DivPlatformFDS::keyOffAffectsArp(int ch) { return true; } +void DivPlatformFDS::setNSFPlay(bool use) { + useNP=use; +} + void DivPlatformFDS::setFlags(unsigned int flags) { if (flags==2) { // Dendy rate=COLOR_PAL*2.0/5.0; @@ -445,6 +486,10 @@ void DivPlatformFDS::setFlags(unsigned int flags) { } chipClock=rate; oscBuf->rate=rate/32; + if (useNP) { + fds_NP->SetClock(rate); + fds_NP->SetRate(rate); + } } void DivPlatformFDS::notifyInsDeletion(void* ins) { @@ -467,7 +512,11 @@ int DivPlatformFDS::init(DivEngine* p, int channels, int sugRate, unsigned int f dumpWrites=false; skipRegisterWrites=false; writeOscBuf=0; - fds=new struct _fds; + if (useNP) { + fds_NP=new xgm::NES_FDS; + } else { + fds=new struct _fds; + } oscBuf=new DivDispatchOscBuffer; for (int i=0; i<1; i++) { isMuted[i]=false; @@ -475,12 +524,16 @@ int DivPlatformFDS::init(DivEngine* p, int channels, int sugRate, unsigned int f setFlags(flags); reset(); - return 5; + return 1; } void DivPlatformFDS::quit() { delete oscBuf; - delete fds; + if (useNP) { + delete fds_NP; + } else { + delete fds; + } } DivPlatformFDS::~DivPlatformFDS() { diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index c7c53aea..1c08e1bb 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -24,6 +24,8 @@ #include "../macroInt.h" #include "../waveSynth.h" +#include "sound/nes_nsfplay/nes_fds.h" + class DivPlatformFDS: public DivDispatch { struct Channel { int freq, baseFreq, pitch, pitch2, prevFreq, note, modFreq, ins; @@ -69,13 +71,19 @@ class DivPlatformFDS: public DivDispatch { DivWaveSynth ws; unsigned char apuType; unsigned char writeOscBuf; + bool useNP; struct _fds* fds; + xgm::NES_FDS* fds_NP; unsigned char regPool[128]; void updateWave(); 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); @@ -88,6 +96,7 @@ class DivPlatformFDS: public DivDispatch { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); + void setNSFPlay(bool use); void setFlags(unsigned int flags); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp index f3e61dec..7599fa85 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp @@ -21,6 +21,8 @@ NES_FDS::NES_FDS () sm[0] = 128; sm[1] = 128; + mask=0; + Reset(); } diff --git a/src/gui/gui.h b/src/gui/gui.h index 49e56f0c..90d909f2 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -789,6 +789,7 @@ class FurnaceGUI { int ym2612Core; int saaCore; int nesCore; + int fdsCore; int mainFont; int patFont; int audioRate; @@ -872,6 +873,7 @@ class FurnaceGUI { ym2612Core(0), saaCore(1), nesCore(0), + fdsCore(0), mainFont(0), patFont(0), audioRate(44100), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 383fa420..b515d653 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -868,6 +868,10 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); ImGui::Combo("##NESCore",&settings.nesCore,nesCores,2); + ImGui::Text("FDS core"); + ImGui::SameLine(); + ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); + ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Appearance")) { @@ -1741,6 +1745,7 @@ void FurnaceGUI::syncSettings() { settings.ym2612Core=e->getConfInt("ym2612Core",0); settings.saaCore=e->getConfInt("saaCore",1); settings.nesCore=e->getConfInt("nesCore",0); + settings.fdsCore=e->getConfInt("fdsCore",0); settings.mainFont=e->getConfInt("mainFont",0); settings.patFont=e->getConfInt("patFont",0); settings.mainFontPath=e->getConfString("mainFontPath",""); @@ -1816,6 +1821,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.ym2612Core,0,1); clampSetting(settings.saaCore,0,1); clampSetting(settings.nesCore,0,1); + clampSetting(settings.fdsCore,0,1); clampSetting(settings.mainFont,0,6); clampSetting(settings.patFont,0,6); clampSetting(settings.patRowsBase,0,1); @@ -1919,6 +1925,7 @@ void FurnaceGUI::commitSettings() { e->setConf("ym2612Core",settings.ym2612Core); e->setConf("saaCore",settings.saaCore); e->setConf("nesCore",settings.nesCore); + e->setConf("fdsCore",settings.fdsCore); e->setConf("mainFont",settings.mainFont); e->setConf("patFont",settings.patFont); e->setConf("mainFontPath",settings.mainFontPath); From 4db2ab96aec85719362de451c72fa751978646a8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 18:15:51 -0500 Subject: [PATCH 5/5] document (NON-WORKING) extended op param effects --- papers/doc/7-systems/opl.md | 34 +++++++++++ papers/doc/7-systems/opll.md | 24 ++++++++ papers/doc/7-systems/opz.md | 100 ++++++++++++++++++++++++++++++++ papers/doc/7-systems/ym2151.md | 38 ++++++++++++ papers/doc/7-systems/ym2610.md | 40 +++++++++++++ papers/doc/7-systems/ym2610b.md | 40 +++++++++++++ papers/doc/7-systems/ym2612.md | 40 +++++++++++++ 7 files changed, 316 insertions(+) create mode 100644 papers/doc/7-systems/opz.md diff --git a/papers/doc/7-systems/opl.md b/papers/doc/7-systems/opl.md index b019e3f9..056591a6 100644 --- a/papers/doc/7-systems/opl.md +++ b/papers/doc/7-systems/opl.md @@ -44,3 +44,37 @@ afterwards everyone moved to Windows and software mixed PCM streaming... - only in 4-op mode (OPL3). - `1Dxx`: set attack of operator 4. - only in 4-op mode (OPL3). +- `2Axy`: set waveform of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` is the value. + - only in OPL2 or higher. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` is the value. +- `53xy`: set VIB of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` determines whether VIB is on. +- `54xy` set KSL of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SUS of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` determines whether SUS is on. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `58xx`: set DR of operator 3. + - only in 4-op mode (OPL3). +- `58xx`: set DR of operator 4. + - only in 4-op mode (OPL3). +- `5Bxy`: set KSR of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` determines whether KSR is on. \ No newline at end of file diff --git a/papers/doc/7-systems/opll.md b/papers/doc/7-systems/opll.md index 2e92171b..4e91c137 100644 --- a/papers/doc/7-systems/opll.md +++ b/papers/doc/7-systems/opll.md @@ -37,3 +37,27 @@ the YM2413 is equipped with the following features: - `19xx`: set attack of all operators. - `1Axx`: set attack of operator 1. - `1Bxx`: set attack of operator 2. +- `50xy`: set AM of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` is the value. +- `53xy`: set VIB of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` determines whether VIB is on. +- `54xy` set KSL of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set EGT of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` determines whether EGT is on. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `5Bxy`: set KSR of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` determines whether KSR is on. \ No newline at end of file diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md new file mode 100644 index 00000000..dd69c85c --- /dev/null +++ b/papers/doc/7-systems/opz.md @@ -0,0 +1,100 @@ +# Yamaha OPZ (YM2414) + +this is the YM2151's little-known successor, used in the Yamaha TX81Z and a few other Yamaha synthesizers. oh, and the Korg Z3 too. + +it adds these features on top of the YM2151: +- 8 waveforms (but they're different from the OPL ones) +- per-channel (possibly) linear volume control separate from TL +- increased multiplier precision (in 1/16ths) +- 4-step envelope generator shift (minimum TL) +- another LFO +- no per-operator key on/off +- fixed frequency mode per operator (kind of like OPN family's extended channel mode but with a bit less precision and for all 8 channels) +- "reverb" effect (actually extends release) + +unlike the YM2151, this chip is officially undocumented. very few efforts have been made to study the chip and document it... +therefore emulation of this chip in Furnace is incomplete and uncertain. + +no plans have been made for TX81Z MIDI passthrough, because: +- Furnace works with register writes rather than MIDI commands +- the MIDI protocol is slow (would not be enough). +- the TX81Z is very slow to process a note on/off or parameter change event. +- the TL range has been reduced to 0-99, but the chip goes from 0-127. + +# effects + +- `10xx`: set noise frequency of channel 8 operator 4. 00 disables noise while 01 to 20 enables it. +- `11xx`: set feedback of channel. +- `12xx`: set operator 1 level. +- `13xx`: set operator 2 level. +- `14xx`: set operator 3 level. +- `15xx`: set operator 4 level. +- `16xy`: set multiplier of operator. + - `x` is the operator (1-4). + - `y` is the mutliplier. +- `17xx`: set LFO speed. +- `18xx`: set LFO waveform. `xx` may be one of the following: + - `00`: saw + - `01`: square + - `02`: triangle + - `03`: noise +- `19xx`: set attack of all operators. +- `1Axx`: set attack of operator 1. +- `1Bxx`: set attack of operator 2. +- `1Cxx`: set attack of operator 3. +- `1Dxx`: set attack of operator 4. +- `28xy`: set reverb of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `29xy`: set EG shift of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `2Axy`: set waveform of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `2Fxx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `3xyy`: set fixed frequency of operator 1/2. + - `x` is the block (0-7 for operator 1; 8-F for operator 2). + - `y` is the frequency. fixed frequency mode will be disabled if this is less than 8. + - the actual frequency is: `y*(2^x)`. +- `4xyy`: set fixed frequency of operator 3/4. + - `x` is the block (0-7 for operator 3; 8-F for operator 4). + - `y` is the frequency. fixed frequency mode will be disabled if this is less than 8. + - the actual frequency is: `y*(2^x)`. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set DT2 of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file diff --git a/papers/doc/7-systems/ym2151.md b/papers/doc/7-systems/ym2151.md index 69eba0fb..2291088e 100644 --- a/papers/doc/7-systems/ym2151.md +++ b/papers/doc/7-systems/ym2151.md @@ -26,3 +26,41 @@ it also was present on several pinball machines and synthesizers of the era, and - `1Bxx`: set attack of operator 2. - `1Cxx`: set attack of operator 3. - `1Dxx`: set attack of operator 4. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set DT2 of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file diff --git a/papers/doc/7-systems/ym2610.md b/papers/doc/7-systems/ym2610.md index 4515fde2..ec405efe 100644 --- a/papers/doc/7-systems/ym2610.md +++ b/papers/doc/7-systems/ym2610.md @@ -57,3 +57,43 @@ its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and 2 different - `x` is the numerator. - `y` is the denominator. - if `x` or `y` are 0 this will disable auto-envelope mode. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SSG-EG of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value (0-8). + - values between 0 and 7 set SSG-EG. + - value 8 disables it. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file diff --git a/papers/doc/7-systems/ym2610b.md b/papers/doc/7-systems/ym2610b.md index e251122c..17f27455 100644 --- a/papers/doc/7-systems/ym2610b.md +++ b/papers/doc/7-systems/ym2610b.md @@ -56,3 +56,43 @@ it is backward compatible with the original chip. - `x` is the numerator. - `y` is the denominator. - if `x` or `y` are 0 this will disable auto-envelope mode. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SSG-EG of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value (0-8). + - values between 0 and 7 set SSG-EG. + - value 8 disables it. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file diff --git a/papers/doc/7-systems/ym2612.md b/papers/doc/7-systems/ym2612.md index b7dd8a16..c32521f2 100644 --- a/papers/doc/7-systems/ym2612.md +++ b/papers/doc/7-systems/ym2612.md @@ -25,3 +25,43 @@ one of two chips that powered the Sega Genesis. It is a six-channel, four-operat - `1Bxx`: set attack of operator 2. - `1Cxx`: set attack of operator 3. - `1Dxx`: set attack of operator 4. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SSG-EG of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value (0-8). + - values between 0 and 7 set SSG-EG. + - value 8 disables it. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file