From 4d8f86a27c85cb5473a113e9f9c74bbcde22deba Mon Sep 17 00:00:00 2001 From: Laurens Holst Date: Sun, 1 May 2022 17:25:27 +0200 Subject: [PATCH 01/26] Fix horizontal scroll direction on macOS. See open Dear ImGUI issue: https://github.com/ocornut/imgui/issues/4019 This patches it for macOS in the local copy of imgui_impl_sdl. --- extern/imgui_patched/imgui_impl_sdl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extern/imgui_patched/imgui_impl_sdl.cpp b/extern/imgui_patched/imgui_impl_sdl.cpp index 2870d2170..c1b2649ca 100644 --- a/extern/imgui_patched/imgui_impl_sdl.cpp +++ b/extern/imgui_patched/imgui_impl_sdl.cpp @@ -300,6 +300,9 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { float wheel_x = (event->wheel.x > 0) ? 1.0f : (event->wheel.x < 0) ? -1.0f : 0.0f; float wheel_y = (event->wheel.y > 0) ? 1.0f : (event->wheel.y < 0) ? -1.0f : 0.0f; +#ifdef __APPLE__ + wheel_x = -wheel_x; +#endif io.AddMouseWheelEvent(wheel_x, wheel_y); return true; } From 4bc70fad7b34de20c11ac4459b49035517cb0685 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 13:41:30 -0500 Subject: [PATCH 02/26] format oops --- papers/format.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/papers/format.md b/papers/format.md index 570529c4e..42488719b 100644 --- a/papers/format.md +++ b/papers/format.md @@ -675,6 +675,26 @@ size | description 1 | parameter 2 1 | parameter 3 1 | parameter 4 + --- | **additional macro mode flags** (>=84) + 1 | volume macro mode + 1 | duty macro mode + 1 | wave macro mode + 1 | pitch macro mode + 1 | extra 1 macro mode + 1 | extra 2 macro mode + 1 | extra 3 macro mode + 1 | alg macro mode + 1 | fb macro mode + 1 | fms macro mode + 1 | ams macro mode + 1 | left panning macro mode + 1 | right panning macro mode + 1 | phase reset macro mode + 1 | extra 4 macro mode + 1 | extra 5 macro mode + 1 | extra 6 macro mode + 1 | extra 7 macro mode + 1 | extra 8 macro mode --- | **extra C64 data** (>=89) 1 | don't test/gate before new note ``` From 7f0074511c3f98c05930d12fe6a190a6949be36c Mon Sep 17 00:00:00 2001 From: Laurens Holst Date: Sun, 1 May 2022 19:57:44 +0200 Subject: [PATCH 03/26] Move renderSamples() to DivDispatch implementations. To prevent rendering samples for systems that are not in use. Additionally, it gives the systems more flexibility to render the samples according to their specific configuration. --- src/engine/dispatch.h | 20 ++++ src/engine/engine.cpp | 122 +++--------------------- src/engine/engine.h | 28 +----- src/engine/fileOps.cpp | 18 ++-- src/engine/platform/abstract.cpp | 16 ++++ src/engine/platform/qsound.cpp | 54 ++++++++++- src/engine/platform/qsound.h | 6 ++ src/engine/platform/x1_010.cpp | 48 +++++++++- src/engine/platform/x1_010.h | 14 ++- src/engine/platform/ym2610.cpp | 85 ++++++++++++++++- src/engine/platform/ym2610.h | 25 ++++- src/engine/platform/ym2610Interface.cpp | 10 +- src/engine/platform/ym2610b.cpp | 5 +- src/engine/platform/ym2610b.h | 3 +- src/engine/vgmOps.cpp | 65 +++++++------ src/gui/stats.cpp | 27 +++--- 16 files changed, 327 insertions(+), 219 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 7e5f998b9..58c9c9b71 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -438,6 +438,26 @@ class DivDispatch { */ virtual const char** getRegisterSheet(); + /** + * Get sample memory buffer. + */ + virtual const void* getSampleMem(int index = 0); + + /** + * Get sample memory capacity. + */ + virtual size_t getSampleMemCapacity(int index = 0); + + /** + * Get sample memory usage. + */ + virtual size_t getSampleMemUsage(int index = 0); + + /** + * Render samples into sample memory. + */ + virtual void renderSamples(); + /** * initialize this DivDispatch. * @param parent the parent DivEngine. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3a0a841d8..279d75226 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -506,118 +506,12 @@ void DivEngine::renderSamples() { song.sample[i]->render(); } - // step 2: allocate ADPCM-A samples - if (adpcmAMem==NULL) adpcmAMem=new unsigned char[16777216]; - - size_t memPos=0; - for (int i=0; ilengthA+255)&(~0xff); - if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { - memPos=(memPos+0xfffff)&0xf00000; + // step 2: render samples to dispatch + for (int i=0; irenderSamples(); } - if (memPos>=16777216) { - logW("out of ADPCM-A memory for sample %d!",i); - break; - } - if (memPos+paddedLen>=16777216) { - memcpy(adpcmAMem+memPos,s->dataA,16777216-memPos); - logW("out of ADPCM-A memory for sample %d!",i); - } else { - memcpy(adpcmAMem+memPos,s->dataA,paddedLen); - } - s->offA=memPos; - memPos+=paddedLen; } - adpcmAMemLen=memPos+256; - - // step 2: allocate ADPCM-B samples - if (adpcmBMem==NULL) adpcmBMem=new unsigned char[16777216]; - - memPos=0; - for (int i=0; ilengthB+255)&(~0xff); - if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { - memPos=(memPos+0xfffff)&0xf00000; - } - if (memPos>=16777216) { - logW("out of ADPCM-B memory for sample %d!",i); - break; - } - if (memPos+paddedLen>=16777216) { - memcpy(adpcmBMem+memPos,s->dataB,16777216-memPos); - logW("out of ADPCM-B memory for sample %d!",i); - } else { - memcpy(adpcmBMem+memPos,s->dataB,paddedLen); - } - s->offB=memPos; - memPos+=paddedLen; - } - adpcmBMemLen=memPos+256; - - // step 4: allocate qsound pcm samples - if (qsoundMem==NULL) qsoundMem=new unsigned char[16777216]; - memset(qsoundMem,0,16777216); - - memPos=0; - for (int i=0; ilength8; - if (length>65536-16) { - length=65536-16; - } - if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { - memPos=(memPos+0xffff)&0xff0000; - } - if (memPos>=16777216) { - logW("out of QSound PCM memory for sample %d!",i); - break; - } - if (memPos+length>=16777216) { - for (unsigned int i=0; i<16777216-(memPos+length); i++) { - qsoundMem[(memPos+i)^0x8000]=s->data8[i]; - } - logW("out of QSound PCM memory for sample %d!",i); - } else { - for (int i=0; idata8[i]; - } - } - s->offQSound=memPos^0x8000; - memPos+=length+16; - } - qsoundMemLen=memPos+256; - - // step 4: allocate x1-010 pcm samples - if (x1_010Mem==NULL) x1_010Mem=new unsigned char[1048576]; - memset(x1_010Mem,0,1048576); - - memPos=0; - for (int i=0; ilength8+4095)&(~0xfff); - // fit sample bank size to 128KB for Seta 2 external bankswitching logic (not emulated yet!) - if (paddedLen>131072) { - paddedLen=131072; - } - if ((memPos&0xfe0000)!=((memPos+paddedLen)&0xfe0000)) { - memPos=(memPos+0x1ffff)&0xfe0000; - } - if (memPos>=1048576) { - logW("out of X1-010 memory for sample %d!",i); - break; - } - if (memPos+paddedLen>=1048576) { - memcpy(x1_010Mem+memPos,s->data8,1048576-memPos); - logW("out of X1-010 memory for sample %d!",i); - } else { - memcpy(x1_010Mem+memPos,s->data8,paddedLen); - } - s->offX1_010=memPos; - memPos+=paddedLen; - } - x1_010MemLen=memPos+256; } String DivEngine::encodeSysDesc(std::vector& desc) { @@ -725,11 +619,11 @@ void DivEngine::createNew(const int* description) { initSongWithDesc(description); } recalcChans(); - renderSamples(); saveLock.unlock(); BUSY_END; initDispatch(); BUSY_BEGIN; + renderSamples(); reset(); BUSY_END; } @@ -967,6 +861,11 @@ DivSample* DivEngine::getSample(int index) { return song.sample[index]; } +DivDispatch* DivEngine::getDispatch(int index) { + if (index<0 || index>=song.systemLen) return NULL; + return disCont[index].dispatch; +} + void DivEngine::setLoops(int loops) { remainingLoops=loops; } @@ -2812,6 +2711,7 @@ bool DivEngine::init() { oscBuf[1]=new float[32768]; initDispatch(); + renderSamples(); reset(); active=true; diff --git a/src/engine/engine.h b/src/engine/engine.h index 2ca301c53..dc1436eed 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -422,6 +422,7 @@ class DivEngine { DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM); DivWavetable* getWave(int index); DivSample* getSample(int index); + DivDispatch* getDispatch(int index); // parse system setup description String encodeSysDesc(std::vector& desc); std::vector decodeSysDesc(String desc); @@ -849,19 +850,6 @@ class DivEngine { // terminate the engine. bool quit(); - unsigned char* adpcmAMem; - size_t adpcmAMemLen; - unsigned char* adpcmBMem; - size_t adpcmBMemLen; - unsigned char* qsoundMem; - size_t qsoundMemLen; - unsigned char* qsoundAMem; - size_t qsoundAMemLen; - unsigned char* dpcmMem; - size_t dpcmMemLen; - unsigned char* x1_010Mem; - size_t x1_010MemLen; - DivEngine(): output(NULL), exportThread(NULL), @@ -935,19 +923,7 @@ class DivEngine { oscSize(1), oscReadPos(0), oscWritePos(0), - tickMult(1), - adpcmAMem(NULL), - adpcmAMemLen(0), - adpcmBMem(NULL), - adpcmBMemLen(0), - qsoundMem(NULL), - qsoundMemLen(0), - qsoundAMem(NULL), - qsoundAMemLen(0), - dpcmMem(NULL), - dpcmMemLen(0), - x1_010Mem(NULL), - x1_010MemLen(0) { + tickMult(1) { memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool)); memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool)); memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int)); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index cfd23e1fb..b543e3e12 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -899,12 +899,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { song.unload(); song=ds; recalcChans(); - renderSamples(); saveLock.unlock(); BUSY_END; if (active) { initDispatch(); - syncReset(); + BUSY_BEGIN; + renderSamples(); + reset(); + BUSY_END; } } catch (EndOfFileException& e) { logE("premature end of file!"); @@ -1596,12 +1598,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { song.unload(); song=ds; recalcChans(); - renderSamples(); saveLock.unlock(); BUSY_END; if (active) { initDispatch(); - syncReset(); + BUSY_BEGIN; + renderSamples(); + reset(); + BUSY_END; } } catch (EndOfFileException& e) { logE("premature end of file!"); @@ -2005,12 +2009,14 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { song.unload(); song=ds; recalcChans(); - renderSamples(); saveLock.unlock(); BUSY_END; if (active) { initDispatch(); - syncReset(); + BUSY_BEGIN; + renderSamples(); + reset(); + BUSY_END; } success=true; } catch (EndOfFileException& e) { diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 91f61fc62..54366e1ba 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -141,6 +141,22 @@ const char** DivDispatch::getRegisterSheet() { return NULL; } +const void* DivDispatch::getSampleMem(int index) { + return NULL; +} + +size_t DivDispatch::getSampleMemCapacity(int index) { + return 0; +} + +size_t DivDispatch::getSampleMemUsage(int index) { + return 0; +} + +void DivDispatch::renderSamples() { + +} + int DivDispatch::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { return 0; } diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index c5d003be5..6e89e5b61 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -268,8 +268,6 @@ const char* DivPlatformQSound::getEffectName(unsigned char effect) { return NULL; } void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t len) { - chip.rom_data = parent->qsoundMem; - chip.rom_mask = 0xffffff; for (size_t h=start; hsong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int length=s->length8; + if (length>65536-16) { + length=65536-16; + } + if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { + memPos=(memPos+0xffff)&0xff0000; + } + if (memPos>=getSampleMemCapacity()) { + logW("out of QSound PCM memory for sample %d!",i); + break; + } + if (memPos+length>=getSampleMemCapacity()) { + for (unsigned int i=0; idata8[i]; + } + logW("out of QSound PCM memory for sample %d!",i); + } else { + for (int i=0; idata8[i]; + } + } + s->offQSound=memPos^0x8000; + memPos+=length+16; + } + sampleMemLen=memPos+256; +} + int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; @@ -651,8 +694,10 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned in chipClock=60000000; rate = qsound_start(&chip, chipClock); - chip.rom_data = (unsigned char*)&chip.rom_mask; - chip.rom_mask = 0; + sampleMem=new unsigned char[getSampleMemCapacity()]; + sampleMemLen=0; + chip.rom_data=sampleMem; + chip.rom_mask=0xffffff; reset(); for (int i=0; i<19; i++) { @@ -662,6 +707,7 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned in } void DivPlatformQSound::quit() { + delete[] sampleMem; for (int i=0; i<19; i++) { delete oscBuf[i]; } diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 0fbce3b32..d12e952ef 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -67,6 +67,8 @@ class DivPlatformQSound: public DivDispatch { int echoDelay; int echoFeedback; + unsigned char* sampleMem; + size_t sampleMemLen; struct qsound_chip chip; unsigned short regPool[512]; @@ -94,6 +96,10 @@ class DivPlatformQSound: public DivDispatch { void poke(std::vector& wlist); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); + const void* getSampleMem(int index = 0); + size_t getSampleMemCapacity(int index = 0); + size_t getSampleMemUsage(int index = 0); + void renderSamples(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); }; diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index a2e28b518..03cd4f6be 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -19,6 +19,7 @@ #include "x1_010.h" #include "../engine.h" +#include "../../ta-log.h" #include //#define rWrite(a,v) pendingWrites[a]=v; @@ -909,6 +910,48 @@ void DivPlatformX1_010::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); } +const void* DivPlatformX1_010::getSampleMem(int index) { + return index == 0 ? sampleMem : 0; +} + +size_t DivPlatformX1_010::getSampleMemCapacity(int index) { + return index == 0 ? 1048576 : 0; +} + +size_t DivPlatformX1_010::getSampleMemUsage(int index) { + return index == 0 ? sampleMemLen : 0; +} + +void DivPlatformX1_010::renderSamples() { + memset(sampleMem,0,getSampleMemCapacity()); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->length8+4095)&(~0xfff); + // fit sample bank size to 128KB for Seta 2 external bankswitching logic (not emulated yet!) + if (paddedLen>131072) { + paddedLen=131072; + } + if ((memPos&0xfe0000)!=((memPos+paddedLen)&0xfe0000)) { + memPos=(memPos+0x1ffff)&0xfe0000; + } + if (memPos>=getSampleMemCapacity()) { + logW("out of X1-010 memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity()) { + memcpy(sampleMem+memPos,s->data8,getSampleMemCapacity()-memPos); + logW("out of X1-010 memory for sample %d!",i); + } else { + memcpy(sampleMem+memPos,s->data8,paddedLen); + } + s->offX1_010=memPos; + memPos+=paddedLen; + } + sampleMemLen=memPos+256; +} + int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; @@ -919,7 +962,9 @@ int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned in oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); - intf.parent=parent; + sampleMem=new unsigned char[getSampleMemCapacity()]; + sampleMemLen=0; + intf.memory=sampleMem; x1_010=new x1_010_core(intf); x1_010->reset(); reset(); @@ -931,6 +976,7 @@ void DivPlatformX1_010::quit() { delete oscBuf[i]; } delete x1_010; + delete[] sampleMem; } DivPlatformX1_010::~DivPlatformX1_010() { diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index 11de4bf4a..939280ef9 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -28,13 +28,13 @@ class DivX1_010Interface: public x1_010_mem_intf { public: - DivEngine* parent; + unsigned char* memory; int sampleBank; virtual u8 read_byte(u32 address) override { - if (parent->x1_010Mem==NULL) return 0; - return parent->x1_010Mem[address & 0xfffff]; + if (memory==NULL) return 0; + return memory[address & 0xfffff]; } - DivX1_010Interface(): parent(NULL), sampleBank(0) {} + DivX1_010Interface(): memory(NULL), sampleBank(0) {} }; class DivPlatformX1_010: public DivDispatch { @@ -115,6 +115,8 @@ class DivPlatformX1_010: public DivDispatch { DivDispatchOscBuffer* oscBuf[16]; bool isMuted[16]; bool stereo=false; + unsigned char* sampleMem; + size_t sampleMemLen; unsigned char sampleBank; DivX1_010Interface intf; x1_010_core* x1_010; @@ -141,6 +143,10 @@ class DivPlatformX1_010: public DivDispatch { void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); + const void* getSampleMem(int index = 0); + size_t getSampleMemCapacity(int index = 0); + size_t getSampleMemUsage(int index = 0); + void renderSamples(); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 5154acc62..3711515aa 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -20,6 +20,7 @@ #include "ym2610.h" #include "sound/ymfm/ymfm.h" #include "../engine.h" +#include "../../ta-log.h" #include #include @@ -245,6 +246,85 @@ const char* regCheatSheetYM2610[]={ NULL }; +const void* DivPlatformYM2610Base::getSampleMem(int index) { + return index == 0 ? adpcmAMem : index == 1 ? adpcmBMem : NULL; +} + +size_t DivPlatformYM2610Base::getSampleMemCapacity(int index) { + return index == 0 ? 16777216 : index == 1 ? 16777216 : 0; +} + +size_t DivPlatformYM2610Base::getSampleMemUsage(int index) { + return index == 0 ? adpcmAMemLen : index == 1 ? adpcmBMemLen : 0; +} + +void DivPlatformYM2610Base::renderSamples() { + memset(adpcmAMem,0,getSampleMemCapacity(0)); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->lengthA+255)&(~0xff); + if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { + memPos=(memPos+0xfffff)&0xf00000; + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of ADPCM-A memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(adpcmAMem+memPos,s->dataA,getSampleMemCapacity(0)-memPos); + logW("out of ADPCM-A memory for sample %d!",i); + } else { + memcpy(adpcmAMem+memPos,s->dataA,paddedLen); + } + s->offA=memPos; + memPos+=paddedLen; + } + adpcmAMemLen=memPos+256; + + memset(adpcmBMem,0,getSampleMemCapacity(1)); + + memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->lengthB+255)&(~0xff); + if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { + memPos=(memPos+0xfffff)&0xf00000; + } + if (memPos>=getSampleMemCapacity(1)) { + logW("out of ADPCM-B memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(1)) { + memcpy(adpcmBMem+memPos,s->dataB,getSampleMemCapacity(1)-memPos); + logW("out of ADPCM-B memory for sample %d!",i); + } else { + memcpy(adpcmBMem+memPos,s->dataB,paddedLen); + } + s->offB=memPos; + memPos+=paddedLen; + } + adpcmBMemLen=memPos+256; +} + +int DivPlatformYM2610Base::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + adpcmAMem=new unsigned char[getSampleMemCapacity(0)]; + adpcmAMemLen=0; + adpcmBMem=new unsigned char[getSampleMemCapacity(1)]; + adpcmBMemLen=0; + iface.adpcmAMem=adpcmAMem; + iface.adpcmBMem=adpcmBMem; + iface.sampleBank=0; + return 0; +} + +void DivPlatformYM2610Base::quit() { + delete[] adpcmAMem; + delete[] adpcmBMem; +} + const char** DivPlatformYM2610::getRegisterSheet() { return regCheatSheetYM2610; } @@ -1185,7 +1265,7 @@ void DivPlatformYM2610::setSkipRegisterWrites(bool value) { } int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { - parent=p; + DivPlatformYM2610Base::init(p, channels, sugRate, flags); dumpWrites=false; skipRegisterWrites=false; for (int i=0; i<14; i++) { @@ -1197,8 +1277,6 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned in for (int i=0; i<14; i++) { oscBuf[i]->rate=rate; } - iface.parent=parent; - iface.sampleBank=0; fm=new ymfm::ym2610(iface); // YM2149, 2MHz ay=new DivPlatformAY8910; @@ -1215,6 +1293,7 @@ void DivPlatformYM2610::quit() { ay->quit(); delete ay; delete fm; + DivPlatformYM2610Base::quit(); } DivPlatformYM2610::~DivPlatformYM2610() { diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index c46bc3c43..45ecb335a 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -27,14 +27,32 @@ class DivYM2610Interface: public ymfm::ymfm_interface { public: - DivEngine* parent; + unsigned char* adpcmAMem; + unsigned char* adpcmBMem; int sampleBank; uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address); void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data); - DivYM2610Interface(): parent(NULL), sampleBank(0) {} + DivYM2610Interface(): adpcmAMem(NULL), adpcmBMem(NULL), sampleBank(0) {} }; -class DivPlatformYM2610: public DivDispatch { +class DivPlatformYM2610Base: public DivDispatch { + protected: + unsigned char* adpcmAMem; + size_t adpcmAMemLen; + unsigned char* adpcmBMem; + size_t adpcmBMemLen; + DivYM2610Interface iface; + + public: + 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(); +}; + +class DivPlatformYM2610: public DivPlatformYM2610Base { protected: const unsigned short chanOffs[4]={ 0x01, 0x02, 0x101, 0x102 @@ -93,7 +111,6 @@ class DivPlatformYM2610: public DivDispatch { std::queue writes; ymfm::ym2610* fm; ymfm::ym2610::output_data fmout; - DivYM2610Interface iface; DivPlatformAY8910* ay; unsigned char regPool[512]; diff --git a/src/engine/platform/ym2610Interface.cpp b/src/engine/platform/ym2610Interface.cpp index 9154b40e7..d442ce347 100644 --- a/src/engine/platform/ym2610Interface.cpp +++ b/src/engine/platform/ym2610Interface.cpp @@ -24,13 +24,11 @@ uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { switch (type) { case ymfm::ACCESS_ADPCM_A: - if (parent->adpcmAMem==NULL) return 0; - if ((address&0xffffff)>=parent->adpcmAMemLen) return 0; - return parent->adpcmAMem[address&0xffffff]; + if (adpcmAMem==NULL) return 0; + return adpcmAMem[address&0xffffff]; case ymfm::ACCESS_ADPCM_B: - if (parent->adpcmBMem==NULL) return 0; - if ((address&0xffffff)>=parent->adpcmBMemLen) return 0; - return parent->adpcmBMem[address&0xffffff]; + if (adpcmBMem==NULL) return 0; + return adpcmBMem[address&0xffffff]; default: return 0; } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index a0f1a6e3f..9c126c2b3 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -1243,7 +1243,7 @@ void DivPlatformYM2610B::setSkipRegisterWrites(bool value) { } int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { - parent=p; + DivPlatformYM2610Base::init(p, channels, sugRate, flags); dumpWrites=false; skipRegisterWrites=false; for (int i=0; i<16; i++) { @@ -1255,8 +1255,6 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; } - iface.parent=parent; - iface.sampleBank=0; fm=new ymfm::ym2610b(iface); // YM2149, 2MHz ay=new DivPlatformAY8910; @@ -1273,6 +1271,7 @@ void DivPlatformYM2610B::quit() { ay->quit(); delete ay; delete fm; + DivPlatformYM2610Base::quit(); } DivPlatformYM2610B::~DivPlatformYM2610B() { diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index df0a83233..d1af3069a 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -26,7 +26,7 @@ #include "ym2610.h" -class DivPlatformYM2610B: public DivDispatch { +class DivPlatformYM2610B: public DivPlatformYM2610Base { protected: const unsigned short chanOffs[6]={ 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 @@ -85,7 +85,6 @@ class DivPlatformYM2610B: public DivDispatch { std::queue writes; ymfm::ym2610b* fm; ymfm::ym2610b::output_data fmout; - DivYM2610Interface iface; unsigned char regPool[512]; unsigned char lastBusy; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 7817c060e..53800a525 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -713,10 +713,10 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { bool writeDACSamples=false; bool writeNESSamples=false; bool writePCESamples=false; - int writeADPCM=0; + DivDispatch* writeADPCM[2]={NULL,NULL}; int writeSegaPCM=0; - int writeX1010=0; - int writeQSound=0; + DivDispatch* writeX1010[2]={NULL,NULL}; + DivDispatch* writeQSound[2]={NULL,NULL}; for (int i=0; ichipClock; willExport[i]=true; - writeX1010=1; + writeX1010[0]=disCont[i].dispatch; } else if (!(hasX1&0x40000000)) { isSecond[i]=true; willExport[i]=true; - writeX1010=2; + writeX1010[1]=disCont[i].dispatch; hasX1|=0x40000000; howManyChips++; } @@ -823,11 +823,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { if (!hasOPNB) { hasOPNB=disCont[i].dispatch->chipClock; willExport[i]=true; - writeADPCM=1; + writeADPCM[0]=disCont[i].dispatch; } else if (!(hasOPNB&0x40000000)) { isSecond[i]=true; willExport[i]=true; - writeADPCM=2; + writeADPCM[1]=disCont[i].dispatch; hasOPNB|=0x40000000; howManyChips++; } @@ -929,11 +929,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { // not be able to handle the 64kb sample bank trick hasQSound=disCont[i].dispatch->chipClock; willExport[i]=true; - writeQSound=1; + writeQSound[0]=disCont[i].dispatch; } else if (!(hasQSound&0x40000000)) { isSecond[i]=true; willExport[i]=false; - writeQSound=2; + writeQSound[1]=disCont[i].dispatch; addWarning("dual QSound is not supported by the VGM format"); } break; @@ -1249,56 +1249,55 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { delete[] pcmMem; } - if (adpcmAMemLen>0) { - for (int i=0; igetSampleMemUsage(0)>0) { w->writeC(0x67); w->writeC(0x66); w->writeC(0x82); - w->writeI((adpcmAMemLen+8)|(i*0x80000000)); - w->writeI(adpcmAMemLen); + w->writeI((writeADPCM[i]->getSampleMemUsage(0)+8)|(i*0x80000000)); + w->writeI(writeADPCM[i]->getSampleMemCapacity(0)); w->writeI(0); - w->write(adpcmAMem,adpcmAMemLen); + w->write(writeADPCM[i]->getSampleMem(0),writeADPCM[i]->getSampleMemUsage(0)); } } - if (adpcmBMemLen>0) { - for (int i=0; igetSampleMemUsage(1)>0) { w->writeC(0x67); w->writeC(0x66); w->writeC(0x83); - w->writeI((adpcmBMemLen+8)|(i*0x80000000)); - w->writeI(adpcmBMemLen); + w->writeI((writeADPCM[i]->getSampleMemUsage(1)+8)|(i*0x80000000)); + w->writeI(writeADPCM[i]->getSampleMemCapacity(1)); w->writeI(0); - w->write(adpcmBMem,adpcmBMemLen); + w->write(writeADPCM[i]->getSampleMem(1),writeADPCM[i]->getSampleMemUsage(1)); } } - if (qsoundMemLen>0) { - // always write a whole bank - unsigned int blockSize=(qsoundMemLen+0xffff)&(~0xffff); - if (blockSize > 0x1000000) { - blockSize = 0x1000000; - } - for (int i=0; igetSampleMemUsage()>0) { + unsigned int blockSize=(writeQSound[i]->getSampleMemUsage()+0xffff)&(~0xffff); + if (blockSize > 0x1000000) { + blockSize = 0x1000000; + } w->writeC(0x67); w->writeC(0x66); w->writeC(0x8F); w->writeI((blockSize+8)|(i*0x80000000)); - w->writeI(0x1000000); + w->writeI(writeQSound[i]->getSampleMemCapacity()); w->writeI(0); - w->write(qsoundMem,blockSize); + w->write(writeQSound[i]->getSampleMem(),blockSize); } } - if (x1_010MemLen>0) { - for (int i=0; igetSampleMemUsage()>0) { w->writeC(0x67); w->writeC(0x66); w->writeC(0x91); - w->writeI((x1_010MemLen+8)|(i*0x80000000)); - w->writeI(x1_010MemLen); + w->writeI((writeX1010[i]->getSampleMemUsage()+8)|(i*0x80000000)); + w->writeI(writeX1010[i]->getSampleMemCapacity()); w->writeI(0); - w->write(x1_010Mem,x1_010MemLen); + w->write(writeX1010[i]->getSampleMem(),writeX1010[i]->getSampleMemUsage()); } } diff --git a/src/gui/stats.cpp b/src/gui/stats.cpp index 274be2eda..35d6cfc49 100644 --- a/src/gui/stats.cpp +++ b/src/gui/stats.cpp @@ -28,22 +28,17 @@ void FurnaceGUI::drawStats() { } if (!statsOpen) return; if (ImGui::Begin("Statistics",&statsOpen)) { - String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024); - String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); - String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); - String x1_010Usage=fmt::sprintf("%d/1024KB",e->x1_010MemLen/1024); - ImGui::Text("ADPCM-A"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str()); - ImGui::Text("ADPCM-B"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); - ImGui::Text("QSound"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->qsoundMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),qsoundUsage.c_str()); - ImGui::Text("X1-010"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->x1_010MemLen)/1048576.0f,ImVec2(-FLT_MIN,0),x1_010Usage.c_str()); + for (int i=0; isong.systemLen; i++) { + DivDispatch* dispatch=e->getDispatch(i); + for (int j=0; dispatch!=NULL && dispatch->getSampleMemCapacity(j)>0; j++) { + size_t capacity=dispatch->getSampleMemCapacity(j); + size_t usage=dispatch->getSampleMemUsage(j); + String usageStr=fmt::sprintf("%d/%dKB",usage/1024,capacity/1024); + ImGui::Text("%s [%d]", e->getSystemName(e->song.system[i]), j); + ImGui::SameLine(); + ImGui::ProgressBar(((float)usage)/((float)capacity),ImVec2(-FLT_MIN,0),usageStr.c_str()); + } + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_STATS; ImGui::End(); From 269f07f5976ba580ca3ee944b4b796206e2ac72c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 17:26:56 -0500 Subject: [PATCH 04/26] these are debug messages --- src/engine/engine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3a0a841d8..ad67a018f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2764,9 +2764,9 @@ bool DivEngine::init() { // set default system preset if (!hasLoadedSomething) { - logI("setting"); + logD("setting default preset"); std::vector preset=decodeSysDesc(getConfString("initialSys","")); - logI("preset size %ld",preset.size()); + logD("preset size %ld",preset.size()); if (preset.size()>0 && (preset.size()&3)==0) { preset.push_back(0); initSongWithDesc(preset.data()); From 2c9bad3b3d03cd229eac0be4224ffb4307b10e8e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 18:29:16 -0500 Subject: [PATCH 05/26] per-channel oscilloscope, part 9 work in progress! --- src/engine/dispatch.h | 4 ++- src/gui/chanOsc.cpp | 58 ++++++++++++++++++++++++++++++++++++++----- src/gui/gui.cpp | 6 +++++ src/gui/gui.h | 6 +++++ 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 7e5f998b9..6071fc47e 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -209,11 +209,13 @@ struct DivRegWrite { struct DivDispatchOscBuffer { unsigned int rate; unsigned short needle; + unsigned short readNeedle; short data[65536]; DivDispatchOscBuffer(): rate(65536), - needle(0) { + needle(0), + readNeedle(0) { memset(data,0,65536*sizeof(short)); } }; diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index f1248ffe9..dc392a884 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -30,15 +30,38 @@ void FurnaceGUI::drawChanOsc() { if (!chanOscOpen) return; ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen)) { - if (ImGui::InputInt("Columns",&chanOscCols,1,1)) { - if (chanOscCols<1) chanOscCols=1; - if (chanOscCols>64) chanOscCols=64; + if (ImGui::BeginTable("ChanOscSettings",3)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Columns"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##COSColumns",&chanOscCols,1,1)) { + if (chanOscCols<1) chanOscCols=1; + if (chanOscCols>64) chanOscCols=64; + } + + ImGui::TableNextColumn(); + ImGui::Text("Size (ms)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputFloat("##COSWinSize",&chanOscWindowSize,1.0f,1.0f)) { + if (chanOscWindowSize<1.0f) chanOscWindowSize=1.0f; + if (chanOscWindowSize>50.0f) chanOscWindowSize=50.0f; + } + + ImGui::TableNextColumn(); + ImGui::Checkbox("Center waveform",&chanOscWaveCorr); + + ImGui::EndTable(); } + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f)); float availY=ImGui::GetContentRegionAvail().y; if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) { std::vector oscBufs; + std::vector oscChans; int chans=e->getTotalChannelCount(); ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); @@ -49,7 +72,10 @@ void FurnaceGUI::drawChanOsc() { for (int i=0; igetOscBuffer(i); - if (buf!=NULL) oscBufs.push_back(buf); + if (buf!=NULL) { + oscBufs.push_back(buf); + oscChans.push_back(i); + } } int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols; @@ -58,13 +84,14 @@ void FurnaceGUI::drawChanOsc() { ImGui::TableNextColumn(); DivDispatchOscBuffer* buf=oscBufs[i]; + int ch=oscChans[i]; if (buf==NULL) { ImGui::Text("Error!"); } else { ImVec2 size=ImGui::GetContentRegionAvail(); size.y=availY/rows; - int displaySize=(buf->rate)/30; + int displaySize=(float)(buf->rate)*(chanOscWindowSize/1000.0f); ImVec2 minArea=window->DC.CursorPos; ImVec2 maxArea=ImVec2( @@ -85,7 +112,26 @@ void FurnaceGUI::drawChanOsc() { waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f)); } } else { - unsigned short needlePos=buf->needle-displaySize; + unsigned short needlePos=buf->needle; + if (chanOscWaveCorr) { + float cutoff=0.01f; + while (buf->readNeedle!=needlePos) { + //float old=chanOscLP1[ch]; + chanOscLP0[ch]+=cutoff*((float)buf->data[buf->readNeedle]-chanOscLP0[ch]); + chanOscLP1[ch]+=cutoff*(chanOscLP0[ch]-chanOscLP1[ch]); + if (chanOscLP1[ch]>=20) { + lastCorrPos[ch]=buf->readNeedle; + } + buf->readNeedle++; + } + needlePos=lastCorrPos[ch]; + /* + for (unsigned short i=0; idata[needlePos--]; + if (buf->data[needlePos]>old) break; + }*/ + } + needlePos-=displaySize; for (unsigned short i=0; i<512; i++) { float x=(float)i/512.0f; float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 6d63d0f50..80fad49e2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4000,6 +4000,8 @@ FurnaceGUI::FurnaceGUI(): oscZoom(0.5f), oscZoomSlider(false), chanOscCols(3), + chanOscWindowSize(20.0f), + chanOscWaveCorr(true), followLog(true), pianoOctaves(7), pianoOptions(false), @@ -4057,4 +4059,8 @@ FurnaceGUI::FurnaceGUI(): memset(patChanSlideY,0,sizeof(float)*(DIV_MAX_CHANS+1)); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); memset(oscValues,0,sizeof(float)*512); + + memset(chanOscLP0,0,sizeof(float)*DIV_MAX_CHANS); + memset(chanOscLP1,0,sizeof(float)*DIV_MAX_CHANS); + memset(lastCorrPos,0,sizeof(short)*DIV_MAX_CHANS); } diff --git a/src/gui/gui.h b/src/gui/gui.h index cf532eef5..520abdc41 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1101,6 +1101,12 @@ class FurnaceGUI { // per-channel oscilloscope int chanOscCols; + float chanOscWindowSize; + bool chanOscWaveCorr; + float chanOscLP0[DIV_MAX_CHANS]; + float chanOscLP1[DIV_MAX_CHANS]; + unsigned short lastNeedlePos[DIV_MAX_CHANS]; + unsigned short lastCorrPos[DIV_MAX_CHANS]; // visualizer float keyHit[DIV_MAX_CHANS]; From 6c517292ddb7713e41ef2837ba8202a4ef995736 Mon Sep 17 00:00:00 2001 From: djmaximum17 <61179162+djmaximum17@users.noreply.github.com> Date: Tue, 19 Apr 2022 07:14:12 -0500 Subject: [PATCH 06/26] Add files via upload --- extern/NSFplay/legacy/2413tone.h | 22 + extern/NSFplay/legacy/281btone.h | 20 + extern/NSFplay/legacy/emu2149.c | 363 ++++++ extern/NSFplay/legacy/emu2149.h | 94 ++ extern/NSFplay/legacy/emu2212.c | 485 ++++++++ extern/NSFplay/legacy/emu2212.h | 77 ++ extern/NSFplay/legacy/emu2413.c | 1461 +++++++++++++++++++++++++ extern/NSFplay/legacy/emu2413.h | 247 +++++ extern/NSFplay/legacy/emutypes.h | 41 + extern/NSFplay/legacy/vrc7tone_ft35.h | 20 + extern/NSFplay/legacy/vrc7tone_ft36.h | 21 + extern/NSFplay/legacy/vrc7tone_kt1.h | 21 + extern/NSFplay/legacy/vrc7tone_kt2.h | 21 + extern/NSFplay/legacy/vrc7tone_mo.h | 20 + extern/NSFplay/legacy/vrc7tone_nuke.h | 21 + extern/NSFplay/legacy/vrc7tone_rw.h | 21 + extern/NSFplay/nes_apu.cpp | 400 +++++++ extern/NSFplay/nes_apu.h | 88 ++ extern/NSFplay/nes_dmc.cpp | 771 +++++++++++++ extern/NSFplay/nes_dmc.h | 129 +++ extern/NSFplay/nes_fds.cpp | 397 +++++++ extern/NSFplay/nes_fds.h | 83 ++ extern/NSFplay/nes_fme7.cpp | 186 ++++ extern/NSFplay/nes_fme7.h | 42 + extern/NSFplay/nes_mmc5.cpp | 422 +++++++ extern/NSFplay/nes_mmc5.h | 74 ++ extern/NSFplay/nes_n106.cpp | 367 +++++++ extern/NSFplay/nes_n106.h | 74 ++ extern/NSFplay/nes_vrc6.cpp | 264 +++++ extern/NSFplay/nes_vrc6.h | 56 + extern/NSFplay/nes_vrc7.cpp | 189 ++++ extern/NSFplay/nes_vrc7.h | 53 + 32 files changed, 6550 insertions(+) create mode 100644 extern/NSFplay/legacy/2413tone.h create mode 100644 extern/NSFplay/legacy/281btone.h create mode 100644 extern/NSFplay/legacy/emu2149.c create mode 100644 extern/NSFplay/legacy/emu2149.h create mode 100644 extern/NSFplay/legacy/emu2212.c create mode 100644 extern/NSFplay/legacy/emu2212.h create mode 100644 extern/NSFplay/legacy/emu2413.c create mode 100644 extern/NSFplay/legacy/emu2413.h create mode 100644 extern/NSFplay/legacy/emutypes.h create mode 100644 extern/NSFplay/legacy/vrc7tone_ft35.h create mode 100644 extern/NSFplay/legacy/vrc7tone_ft36.h create mode 100644 extern/NSFplay/legacy/vrc7tone_kt1.h create mode 100644 extern/NSFplay/legacy/vrc7tone_kt2.h create mode 100644 extern/NSFplay/legacy/vrc7tone_mo.h create mode 100644 extern/NSFplay/legacy/vrc7tone_nuke.h create mode 100644 extern/NSFplay/legacy/vrc7tone_rw.h create mode 100644 extern/NSFplay/nes_apu.cpp create mode 100644 extern/NSFplay/nes_apu.h create mode 100644 extern/NSFplay/nes_dmc.cpp create mode 100644 extern/NSFplay/nes_dmc.h create mode 100644 extern/NSFplay/nes_fds.cpp create mode 100644 extern/NSFplay/nes_fds.h create mode 100644 extern/NSFplay/nes_fme7.cpp create mode 100644 extern/NSFplay/nes_fme7.h create mode 100644 extern/NSFplay/nes_mmc5.cpp create mode 100644 extern/NSFplay/nes_mmc5.h create mode 100644 extern/NSFplay/nes_n106.cpp create mode 100644 extern/NSFplay/nes_n106.h create mode 100644 extern/NSFplay/nes_vrc6.cpp create mode 100644 extern/NSFplay/nes_vrc6.h create mode 100644 extern/NSFplay/nes_vrc7.cpp create mode 100644 extern/NSFplay/nes_vrc7.h diff --git a/extern/NSFplay/legacy/2413tone.h b/extern/NSFplay/legacy/2413tone.h new file mode 100644 index 000000000..703979584 --- /dev/null +++ b/extern/NSFplay/legacy/2413tone.h @@ -0,0 +1,22 @@ +/* YM2413 tone by Mitsutaka Okazaki, 2020 */ +/* https://github.com/digital-sound-antiques/emu2413/blob/d2b9c8dfc5e84b7f8a535fdfee0149ac7bd84ca2/emu2413.c#L34 + */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0: Original +0x71, 0x61, 0x1e, 0x17, 0xd0, 0x78, 0x00, 0x17, // 1: Violin +0x13, 0x41, 0x1a, 0x0d, 0xd8, 0xf7, 0x23, 0x13, // 2: Guitar +0x13, 0x01, 0x99, 0x00, 0xf2, 0xc4, 0x21, 0x23, // 3: Piano +0x11, 0x61, 0x0e, 0x07, 0x8d, 0x64, 0x70, 0x27, // 4: Flute +0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28, // 5: Clarinet +0x31, 0x22, 0x16, 0x05, 0xe0, 0x71, 0x00, 0x18, // 6: Oboe +0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07, // 7: Trumpet +0x33, 0x21, 0x2d, 0x13, 0xb0, 0x70, 0x00, 0x07, // 8: Organ +0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, // 9: Horn +0x41, 0x61, 0x0b, 0x18, 0x85, 0xf0, 0x81, 0x07, // A: Synthesizer +0x33, 0x01, 0x83, 0x11, 0xea, 0xef, 0x10, 0x04, // B: Harpsichord +0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12, // C: Vibraphone +0x61, 0x50, 0x0c, 0x05, 0xd2, 0xf5, 0x40, 0x42, // D: Synthsizer Bass +0x01, 0x01, 0x55, 0x03, 0xe9, 0x90, 0x03, 0x02, // E: Acoustic Bass +0x41, 0x41, 0x89, 0x03, 0xf1, 0xe4, 0xc0, 0x13, // F: Electric Guitar +0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d, // R: Bass Drum (from VRC7) +0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68, // R: High-Hat(M) / Snare Drum(C) (from VRC7) +0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55, // R: Tom-tom(M) / Top Cymbal(C) (from VRC7) diff --git a/extern/NSFplay/legacy/281btone.h b/extern/NSFplay/legacy/281btone.h new file mode 100644 index 000000000..bb0068bab --- /dev/null +++ b/extern/NSFplay/legacy/281btone.h @@ -0,0 +1,20 @@ +/* YMF281B tone by Chabin (4/10/2004) */ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16, +0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23, +0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3, +0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17, +0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, +0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07, +0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17, +0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28, +0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07, +0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11, +0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26, +0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22, +0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4, +0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff, +0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5, +0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, +0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/emu2149.c b/extern/NSFplay/legacy/emu2149.c new file mode 100644 index 000000000..fa5142f73 --- /dev/null +++ b/extern/NSFplay/legacy/emu2149.c @@ -0,0 +1,363 @@ +/**************************************************************************** + + emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 + + 2001 04-28 : Version 1.00beta -- 1st Beta Release. + 2001 08-14 : Version 1.10 + 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). + 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. + 2002 10-13 : Version 1.14 -- Fixed the envelope unit. + 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask + 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope + frequency register is written before key-on. + + References: + psg.vhd -- 2000 written by Kazuhiro Tsujikawa. + s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). + ay8910.c -- 1998-2001 Author unknown (MAME). + MSX-Datapack -- 1991 ASCII Corp. + AY-3-8910 data sheet + +*****************************************************************************/ +#include +#include +#include +#include "emu2149.h" + +static e_uint32 voltbl[2][32] = { + {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, + 0x0B, 0x0D, 0x0F, 0x12, + 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, + 0xB4, 0xD6, 0xEB, 0xFF}, + {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, + 0x0B, 0x0B, 0x0F, 0x0F, + 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, + 0xB4, 0xB4, 0xFF, 0xFF} +}; + +#define GETA_BITS 24 + +static void +internal_refresh (PSG * psg) +{ + if (psg->quality) + { + psg->base_incr = 1 << GETA_BITS; + psg->realstep = (e_uint32) ((1 << 31) / psg->rate); + psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 16)); + psg->psgtime = 0; + } + else + { + psg->base_incr = + (e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (16 * psg->rate)); + } +} + +EMU2149_API void +PSG_set_rate (PSG * psg, e_uint32 r) +{ + psg->rate = r ? r : 44100; + internal_refresh (psg); +} + +EMU2149_API void +PSG_set_quality (PSG * psg, e_uint32 q) +{ + psg->quality = q; + internal_refresh (psg); +} + +EMU2149_API PSG * +PSG_new (e_uint32 c, e_uint32 r) +{ + PSG *psg; + + psg = (PSG *) malloc (sizeof (PSG)); + if (psg == NULL) + return NULL; + + PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); + psg->clk = c; + psg->rate = r ? r : 44100; + PSG_set_quality (psg, 0); + + return psg; +} + +EMU2149_API void +PSG_setVolumeMode (PSG * psg, int type) +{ + switch (type) + { + case 1: + psg->voltbl = voltbl[EMU2149_VOL_YM2149]; + break; + case 2: + psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; + break; + default: + psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; + break; + } +} + +EMU2149_API e_uint32 +PSG_setMask (PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask = mask; + } + return ret; +} + +EMU2149_API e_uint32 +PSG_toggleMask (PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask ^= mask; + } + return ret; +} + +EMU2149_API void +PSG_reset (PSG * psg) +{ + int i; + + psg->base_count = 0; + + for (i = 0; i < 3; i++) + { + psg->cout[i] = 0; + psg->count[i] = 0x1000; + psg->freq[i] = 0; + psg->edge[i] = 0; + psg->volume[i] = 0; + } + + psg->mask = 0; + + for (i = 0; i < 16; i++) + psg->reg[i] = 0; + psg->adr = 0; + + psg->noise_seed = 0xffff; + psg->noise_count = 0x40; + psg->noise_freq = 0; + + psg->env_volume = 0; + psg->env_ptr = 0; + psg->env_freq = 0; + psg->env_count = 0; + psg->env_pause = 1; + + psg->out = 0; +} + +EMU2149_API void +PSG_delete (PSG * psg) +{ + free (psg); +} + +EMU2149_API e_uint8 +PSG_readIO (PSG * psg) +{ + return (e_uint8) (psg->reg[psg->adr]); +} + +EMU2149_API e_uint8 +PSG_readReg (PSG * psg, e_uint32 reg) +{ + return (e_uint8) (psg->reg[reg & 0x1f]); + +} + +EMU2149_API void +PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val) +{ + if (adr & 1) + PSG_writeReg (psg, psg->adr, val); + else + psg->adr = val & 0x1f; +} + +INLINE static e_int16 +calc (PSG * psg) +{ + + int i, noise; + e_uint32 incr; + e_int32 mix = 0; + + psg->base_count += psg->base_incr; + incr = (psg->base_count >> GETA_BITS); + psg->base_count &= (1 << GETA_BITS) - 1; + + /* Envelope */ + psg->env_count += incr; + while (psg->env_count>=0x10000 && psg->env_freq!=0) + { + if (!psg->env_pause) + { + if(psg->env_face) + psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; + else + psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; + } + + if (psg->env_ptr & 0x20) /* if carry or borrow */ + { + if (psg->env_continue) + { + if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; + if (psg->env_hold) psg->env_pause = 1; + psg->env_ptr = psg->env_face?0:0x1f; + } + else + { + psg->env_pause = 1; + psg->env_ptr = 0; + } + } + + psg->env_count -= psg->env_freq; + } + + /* Noise */ + psg->noise_count += incr; + if (psg->noise_count & 0x40) + { + if (psg->noise_seed & 1) + psg->noise_seed ^= 0x24000; + psg->noise_seed >>= 1; + psg->noise_count -= psg->noise_freq; + } + noise = psg->noise_seed & 1; + + /* Tone */ + for (i = 0; i < 3; i++) + { + psg->count[i] += incr; + if (psg->count[i] & 0x1000) + { + if (psg->freq[i] > 1) + { + psg->edge[i] = !psg->edge[i]; + psg->count[i] -= psg->freq[i]; + } + else + { + psg->edge[i] = 1; + } + } + + psg->cout[i] = 0; // maintaining cout for stereo mix + + if (psg->mask&PSG_MASK_CH(i)) + continue; + + if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) + { + if (!(psg->volume[i] & 32)) + psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; + else + psg->cout[i] = psg->voltbl[psg->env_ptr]; + + mix += psg->cout[i]; + } + } + + return (e_int16) mix; +} + +EMU2149_API e_int16 +PSG_calc (PSG * psg) +{ + if (!psg->quality) + return (e_int16) (calc (psg) << 4); + + /* Simple rate converter */ + while (psg->realstep > psg->psgtime) + { + psg->psgtime += psg->psgstep; + psg->out += calc (psg); + psg->out >>= 1; + } + + psg->psgtime = psg->psgtime - psg->realstep; + + return (e_int16) (psg->out << 4); +} + +EMU2149_API void +PSG_writeReg (PSG * psg, e_uint32 reg, e_uint32 val) +{ + int c; + + if (reg > 15) return; + + psg->reg[reg] = (e_uint8) (val & 0xff); + switch (reg) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + c = reg >> 1; + psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; + break; + + case 6: + psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); + break; + + case 7: + psg->tmask[0] = (val & 1); + psg->tmask[1] = (val & 2); + psg->tmask[2] = (val & 4); + psg->nmask[0] = (val & 8); + psg->nmask[1] = (val & 16); + psg->nmask[2] = (val & 32); + break; + + case 8: + case 9: + case 10: + psg->volume[reg - 8] = val << 1; + + break; + + case 11: + case 12: + psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; + break; + + case 13: + psg->env_continue = (val >> 3) & 1; + psg->env_attack = (val >> 2) & 1; + psg->env_alternate = (val >> 1) & 1; + psg->env_hold = val & 1; + psg->env_face = psg->env_attack; + psg->env_pause = 0; + psg->env_count = 0x10000 - psg->env_freq; + psg->env_ptr = psg->env_face?0:0x1f; + break; + + case 14: + case 15: + default: + break; + } + + return; +} \ No newline at end of file diff --git a/extern/NSFplay/legacy/emu2149.h b/extern/NSFplay/legacy/emu2149.h new file mode 100644 index 000000000..a2fba126b --- /dev/null +++ b/extern/NSFplay/legacy/emu2149.h @@ -0,0 +1,94 @@ +/* emu2149.h */ +#ifndef _EMU2149_H_ +#define _EMU2149_H_ +#include "emutypes.h" + +#ifdef EMU2149_DLL_EXPORTS +#define EMU2149_API __declspec(dllexport) +#elif EMU2149_DLL_IMPORTS +#define EMU2149_API __declspec(dllimport) +#else +#define EMU2149_API +#endif + +#define EMU2149_VOL_DEFAULT 1 +#define EMU2149_VOL_YM2149 0 +#define EMU2149_VOL_AY_3_8910 1 + +#define PSG_MASK_CH(x) (1<<(x)) + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct __PSG + { + + /* Volume Table */ + e_uint32 *voltbl; + + e_uint8 reg[0x20]; + e_int32 out; + e_int32 cout[3]; + + e_uint32 clk, rate, base_incr, quality; + + e_uint32 count[3]; + e_uint32 volume[3]; + e_uint32 freq[3]; + e_uint32 edge[3]; + e_uint32 tmask[3]; + e_uint32 nmask[3]; + e_uint32 mask; + + e_uint32 base_count; + + e_uint32 env_volume; + e_uint32 env_ptr; + e_uint32 env_face; + + e_uint32 env_continue; + e_uint32 env_attack; + e_uint32 env_alternate; + e_uint32 env_hold; + e_uint32 env_pause; + e_uint32 env_reset; + + e_uint32 env_freq; + e_uint32 env_count; + + e_uint32 noise_seed; + e_uint32 noise_count; + e_uint32 noise_freq; + + /* rate converter */ + e_uint32 realstep; + e_uint32 psgtime; + e_uint32 psgstep; + + /* I/O Ctrl */ + e_uint32 adr; + + } + PSG; + + EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q); + EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r); + EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate); + EMU2149_API void PSG_reset (PSG *); + EMU2149_API void PSG_delete (PSG *); + EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val); + EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val); + EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg); + EMU2149_API e_uint8 PSG_readIO (PSG * psg); + EMU2149_API e_int16 PSG_calc (PSG *); + EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); + EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask); + EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emu2212.c b/extern/NSFplay/legacy/emu2212.c new file mode 100644 index 000000000..88d93382c --- /dev/null +++ b/extern/NSFplay/legacy/emu2212.c @@ -0,0 +1,485 @@ +/**************************************************************************** + + emu2212.c -- S.C.C. emulator by Mitsutaka Okazaki 2001 + + 2001 09-30 : Version 1.00 + 2001 10-03 : Version 1.01 -- Added SCC_set_quality(). + 2002 02-14 : Version 1.10 -- Added SCC_writeReg(), SCC_set_type(). + Fixed SCC_write(). + 2002 02-17 : Version 1.11 -- Fixed SCC_write(). + 2002 03-02 : Version 1.12 -- Removed SCC_init & SCC_close. + 2003 09-19 : Version 1.13 -- Added SCC_setMask() and SCC_toggleMask() + 2004 10-21 : Version 1.14 -- Fixed the problem where SCC+ is disabled. + + Registar map for SCC_writeReg() + + $00-1F : WaveTable CH.A + $20-3F : WaveTable CH.B + $40-5F : WaveTable CH.C + $60-7F : WaveTable CH.D&E(SCC), CH.D(SCC+) + $80-9F : WaveTable CH.E + + $C0 : CH.A Freq(L) + $C1 : CH.A Freq(H) + $C2 : CH.B Freq(L) + $C3 : CH.B Freq(H) + $C4 : CH.C Freq(L) + $C5 : CH.C Freq(H) + $C6 : CH.D Freq(L) + $C7 : CH.D Freq(H) + $C8 : CH.E Freq(L) + $C9 : CH.E Freq(H) + + $D0 : CH.A Volume + $D1 : CH.B Volume + $D2 : CH.C Volume + $D3 : CH.D Volume + $D4 : CH.E Volume + + $E0 : Bit0 = 0:SCC, 1:SCC+ + $E1 : CH mask + $E2 : Extra Flags + +*****************************************************************************/ +#include +#include +#include +#include "emu2212.h" + +#define GETA_BITS 22 + +static void +internal_refresh (SCC * scc) +{ + if (scc->quality) + { + scc->base_incr = 2 << GETA_BITS; + scc->realstep = (e_uint32) ((1 << 31) / scc->rate); + scc->sccstep = (e_uint32) ((1 << 31) / (scc->clk / 2)); + scc->scctime = 0; + } + else + { + scc->base_incr = (e_uint32) ((double) scc->clk * (1 << GETA_BITS) / scc->rate); + } +} + +EMU2212_API e_uint32 +SCC_setMask (SCC *scc, e_uint32 mask) +{ + e_uint32 ret = 0; + if(scc) + { + ret = scc->mask; + scc->mask = mask; + } + return ret; +} + +EMU2212_API e_uint32 +SCC_toggleMask (SCC *scc, e_uint32 mask) +{ + e_uint32 ret = 0; + if(scc) + { + ret = scc->mask; + scc->mask ^= mask; + } + return ret; +} + +EMU2212_API void +SCC_set_quality (SCC * scc, e_uint32 q) +{ + scc->quality = q; + internal_refresh (scc); +} + +EMU2212_API void +SCC_set_rate (SCC * scc, e_uint32 r) +{ + scc->rate = r ? r : 44100; + internal_refresh (scc); +} + +EMU2212_API SCC * +SCC_new (e_uint32 c, e_uint32 r) +{ + SCC *scc; + + scc = (SCC *) malloc (sizeof (SCC)); + if (scc == NULL) + return NULL; + memset(scc, 0, sizeof (SCC)); + + scc->clk = c; + scc->rate = r ? r : 44100; + SCC_set_quality (scc, 0); + scc->type = SCC_ENHANCED; + return scc; +} + +EMU2212_API void +SCC_reset (SCC * scc) +{ + int i, j; + + if (scc == NULL) + return; + + scc->mode = 0; + scc->active = 0; + scc->base_adr = 0x9000; + + for (i = 0; i < 5; i++) + { + for (j = 0; j < 5; j++) + scc->wave[i][j] = 0; + scc->count[i] = 0; + scc->freq[i] = 0; + scc->phase[i] = 0; + scc->volume[i] = 0; + scc->offset[i] = 0; + scc->rotate[i] = 0; + } + + memset(scc->reg,0,0x100-0xC0); + + scc->mask = 0; + + scc->ch_enable = 0xff; + scc->ch_enable_next = 0xff; + + scc->cycle_4bit = 0; + scc->cycle_8bit = 0; + scc->refresh = 0; + + scc->out = 0; + scc->prev = 0; + scc->next = 0; + + return; +} + +EMU2212_API void +SCC_delete (SCC * scc) +{ + if (scc != NULL) + free (scc); +} + +INLINE static e_int16 +calc (SCC * scc) +{ + int i; + e_int32 mix = 0; + + for (i = 0; i < 5; i++) + { + scc->count[i] = (scc->count[i] + scc->incr[i]); + + if (scc->count[i] & (1 << (GETA_BITS + 5))) + { + scc->count[i] &= ((1 << (GETA_BITS + 5)) - 1); + scc->offset[i] = (scc->offset[i] + 31) & scc->rotate[i]; + scc->ch_enable &= ~(1 << i); + scc->ch_enable |= scc->ch_enable_next & (1 << i); + } + + if (scc->ch_enable & (1 << i)) + { + scc->phase[i] = ((scc->count[i] >> (GETA_BITS)) + scc->offset[i]) & 0x1F; + if(!(scc->mask&SCC_MASK_CH(i))) + mix += ((((e_int8) (scc->wave[i][scc->phase[i]]) * (e_int8) scc->volume[i]))) >> 4; + } + } + + return (e_int16) (mix << 4); +} + +EMU2212_API e_int16 +SCC_calc (SCC * scc) +{ + if (!scc->quality) + return calc (scc); + + while (scc->realstep > scc->scctime) + { + scc->scctime += scc->sccstep; + scc->prev = scc->next; + scc->next = calc (scc); + } + + scc->scctime -= scc->realstep; + scc->out = (e_int16) (((double) scc->next * (scc->sccstep - scc->scctime) + (double) scc->prev * scc->scctime) / scc->sccstep); + + return (e_int16) (scc->out); +} + +EMU2212_API e_uint32 +SCC_readReg (SCC * scc, e_uint32 adr) +{ + if (adr < 0xA0) + return scc->wave[adr >> 5][adr & 0x1f]; + else if( 0xC0 < adr && adr < 0xF0 ) + return scc->reg[adr-0xC0]; + else + return 0; +} + +EMU2212_API void +SCC_writeReg (SCC * scc, e_uint32 adr, e_uint32 val) +{ + int ch; + e_uint32 freq; + + adr &= 0xFF; + + if (adr < 0xA0) + { + ch = (adr & 0xF0) >> 5; + if (!scc->rotate[ch]) + { + scc->wave[ch][adr & 0x1F] = (e_int8) val; + if (scc->mode == 0 && ch == 3) + scc->wave[4][adr & 0x1F] = (e_int8) val; + } + } + else if (0xC0 <= adr && adr <= 0xC9) + { + scc->reg[adr-0xC0] = val; + ch = (adr & 0x0F) >> 1; + if (adr & 1) + scc->freq[ch] = ((val & 0xF) << 8) | (scc->freq[ch] & 0xFF); + else + scc->freq[ch] = (scc->freq[ch] & 0xF00) | (val & 0xFF); + + if (scc->refresh) + scc->count[ch] = 0; + freq = scc->freq[ch]; + if (scc->cycle_8bit) + freq &= 0xFF; + if (scc->cycle_4bit) + freq >>= 8; + if (freq <= 8) + scc->incr[ch] = 0; + else + scc->incr[ch] = scc->base_incr / (freq + 1); + } + else if (0xD0 <= adr && adr <= 0xD4) + { + scc->reg[adr-0xC0] = val; + scc->volume[adr & 0x0F] = (e_uint8) (val & 0xF); + } + else if (adr == 0xE0) + { + scc->reg[adr-0xC0] = val; + scc->mode = (e_uint8) val & 1; + } + else if (adr == 0xE1) + { + scc->reg[adr-0xC0] = val; + scc->ch_enable_next = (e_uint8) val & 0x1F; + } + else if (adr == 0xE2) + { + scc->reg[adr-0xC0] = val; + scc->cycle_4bit = val & 1; + scc->cycle_8bit = val & 2; + scc->refresh = val & 32; + if (val & 64) + for (ch = 0; ch < 5; ch++) + scc->rotate[ch] = 0x1F; + else + for (ch = 0; ch < 5; ch++) + scc->rotate[ch] = 0; + if (val & 128) + scc->rotate[3] = scc->rotate[4] = 0x1F; + } + + return; +} + +INLINE static void +write_standard (SCC * scc, e_uint32 adr, e_uint32 val) +{ + adr &= 0xFF; + + if (adr < 0x80) /* wave */ + { + SCC_writeReg (scc, adr, val); + } + else if (adr < 0x8A) /* freq */ + { + SCC_writeReg (scc, adr + 0xC0 - 0x80, val); + } + else if (adr < 0x8F) /* volume */ + { + SCC_writeReg (scc, adr + 0xD0 - 0x8A, val); + } + else if (adr == 0x8F) /* ch enable */ + { + SCC_writeReg (scc, 0xE1, val); + } + else if (0xE0 <= adr) /* flags */ + { + SCC_writeReg (scc, 0xE2, val); + } +} + +INLINE static void +write_enhanced (SCC * scc, e_uint32 adr, e_uint32 val) +{ + adr &= 0xFF; + + if (adr < 0xA0) /* wave */ + { + SCC_writeReg (scc, adr, val); + } + else if (adr < 0xAA) /* freq */ + { + SCC_writeReg (scc, adr + 0xC0 - 0xA0, val); + } + else if (adr < 0xAF) /* volume */ + { + SCC_writeReg (scc, adr + 0xD0 - 0xAA, val); + } + else if (adr == 0xAF) /* ch enable */ + { + SCC_writeReg (scc, 0xE1, val); + } + else if (0xC0 <= adr && adr <= 0xDF) /* flags */ + { + SCC_writeReg (scc, 0xE2, val); + } +} + +INLINE static e_uint32 +read_enhanced (SCC * scc, e_uint32 adr) +{ + adr &= 0xFF; + if (adr < 0xA0) + return SCC_readReg (scc, adr); + else if (adr < 0xAA) + return SCC_readReg (scc, adr + 0xC0 - 0xA0); + else if (adr < 0xAF) + return SCC_readReg (scc, adr + 0xD0 - 0xAA); + else if (adr == 0xAF) + return SCC_readReg (scc, 0xE1); + else if (0xC0 <= adr && adr <= 0xDF) + return SCC_readReg (scc, 0xE2); + else + return 0; +} + +INLINE static e_uint32 +read_standard (SCC * scc, e_uint32 adr) +{ + adr &= 0xFF; + if(adr<0x80) + return SCC_readReg (scc, adr); + else if (0xA0<=adr&&adr<=0xBF) + return SCC_readReg (scc, 0x80+(adr&0x1F)); + else if (adr < 0x8A) + return SCC_readReg (scc, adr + 0xC0 - 0x80); + else if (adr < 0x8F) + return SCC_readReg (scc, adr + 0xD0 - 0x8A); + else if (adr == 0x8F) + return SCC_readReg (scc, 0xE1); + else if (0xE0 <= adr) + return SCC_readReg (scc, 0xE2); + else return 0; +} + +EMU2212_API e_uint32 +SCC_read (SCC * scc, e_uint32 adr) +{ + if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) + return (scc->base_adr>>8)&0x20; + + if( adr < scc->base_adr ) return 0; + adr -= scc->base_adr; + + if( adr == 0 ) + { + if(scc->mode) return 0x80; else return 0x3F; + } + + if(!scc->active||adr<0x800||0x8FFtype) + { + case SCC_STANDARD: + return read_standard (scc, adr); + break; + case SCC_ENHANCED: + if(!scc->mode) + return read_standard (scc, adr); + else + return read_enhanced (scc, adr); + break; + default: + break; + } + + return 0; +} + +EMU2212_API void +SCC_write (SCC * scc, e_uint32 adr, e_uint32 val) +{ + val = val & 0xFF; + + if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) + { + scc->base_adr = 0x9000 | ((val&0x20)<<8); + return; + } + + if( adr < scc->base_adr ) return; + adr -= scc->base_adr; + + if(adr == 0) + { + if( val == 0x3F ) + { + scc->mode = 0; + scc->active = 1; + } + else if( val&0x80 && scc->type == SCC_ENHANCED) + { + scc->mode = 1; + scc->active = 1; + } + else + { + scc->mode = 0; + scc->active = 0; + } + return; + } + + if(!scc->active||adr<0x800||0x8FFtype) + { + case SCC_STANDARD: + write_standard (scc, adr, val); + break; + case SCC_ENHANCED: + if(scc->mode) + write_enhanced (scc, adr, val); + else + write_standard (scc, adr, val); + default: + break; + } + + return; +} + +EMU2212_API void +SCC_set_type (SCC * scc, e_uint32 type) +{ + scc->type = type; +} diff --git a/extern/NSFplay/legacy/emu2212.h b/extern/NSFplay/legacy/emu2212.h new file mode 100644 index 000000000..b79e0c360 --- /dev/null +++ b/extern/NSFplay/legacy/emu2212.h @@ -0,0 +1,77 @@ +#ifndef _EMU2212_H_ +#define _EMU2212_H_ + +#ifdef EMU2212_DLL_EXPORTS + #define EMU2212_API __declspec(dllexport) +#elif defined(EMU2212_DLL_IMPORTS) + #define EMU2212_API __declspec(dllimport) +#else + #define EMU2212_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "emutypes.h" + +#define SCC_STANDARD 0 +#define SCC_ENHANCED 1 + +#define SCC_MASK_CH(x) (1<<(x)) + +typedef struct __SCC { + + e_uint32 clk, rate ,base_incr, quality ; + + e_int32 out, prev, next; + e_uint32 type ; + e_uint32 mode ; + e_uint32 active; + e_uint32 base_adr; + e_uint32 mask ; + + e_uint32 realstep ; + e_uint32 scctime ; + e_uint32 sccstep ; + + e_uint32 incr[5] ; + + e_int8 wave[5][32] ; + + e_uint32 count[5] ; + e_uint32 freq[5] ; + e_uint32 phase[5] ; + e_uint32 volume[5] ; + e_uint32 offset[5] ; + e_uint8 reg[0x100-0xC0]; + + int ch_enable ; + int ch_enable_next ; + + int cycle_4bit ; + int cycle_8bit ; + int refresh ; + int rotate[5] ; + +} SCC ; + + +EMU2212_API SCC *SCC_new(e_uint32 c, e_uint32 r) ; +EMU2212_API void SCC_reset(SCC *scc) ; +EMU2212_API void SCC_set_rate(SCC *scc, e_uint32 r); +EMU2212_API void SCC_set_quality(SCC *scc, e_uint32 q) ; +EMU2212_API void SCC_set_type(SCC *scc, e_uint32 type) ; +EMU2212_API void SCC_delete(SCC *scc) ; +EMU2212_API e_int16 SCC_calc(SCC *scc) ; +EMU2212_API void SCC_write(SCC *scc, e_uint32 adr, e_uint32 val) ; +EMU2212_API void SCC_writeReg(SCC *scc, e_uint32 adr, e_uint32 val) ; +EMU2212_API e_uint32 SCC_read(SCC *scc, e_uint32 adr) ; +EMU2212_API e_uint32 SCC_setMask(SCC *scc, e_uint32 adr) ; +EMU2212_API e_uint32 SCC_toggleMask(SCC *scc, e_uint32 adr) ; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emu2413.c b/extern/NSFplay/legacy/emu2413.c new file mode 100644 index 000000000..2ced98f86 --- /dev/null +++ b/extern/NSFplay/legacy/emu2413.c @@ -0,0 +1,1461 @@ +/** + * emu2413 v1.5.2 + * https://github.com/digital-sound-antiques/emu2413 + * Copyright (C) 2020 Mitsutaka Okazaki + * + * This source refers to the following documents. The author would like to thank all the authors who have + * contributed to the writing of them. + * - [YM2413 notes](http://www.smspower.org/Development/YM2413) by andete + * - ymf262.c by Jarek Burczynski + * - [VRC7 presets](https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#opll_vrc7_patch_format) by Nuke.YKT + * - YMF281B presets by Chabin + */ +#include "emu2413.h" +#include +#include +#include +#include + +#ifndef INLINE +#if defined(_MSC_VER) +#define INLINE __inline +#elif defined(__GNUC__) +#define INLINE __inline__ +#else +#define INLINE inline +#endif +#endif + +#define _PI_ 3.14159265358979323846264338327950288 + +#define OPLL_TONE_NUM 9 +static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 8] = { + { +#include "vrc7tone_nuke.h" + }, + { +#include "vrc7tone_rw.h" + }, + { +#include "vrc7tone_ft36.h" + }, + { +#include "vrc7tone_ft35.h" + }, + { +#include "vrc7tone_mo.h" + }, + { +#include "vrc7tone_kt2.h" + }, + { +#include "vrc7tone_kt1.h" + }, + { +#include "2413tone.h" + }, + { +#include "281btone.h" + }, +}; + +/* phase increment counter */ +#define DP_BITS 19 +#define DP_WIDTH (1 << DP_BITS) +#define DP_BASE_BITS (DP_BITS - PG_BITS) + +/* dynamic range of envelope output */ +#define EG_STEP 0.375 +#define EG_BITS 7 +#define EG_MUTE ((1 << EG_BITS) - 1) +#define EG_MAX (EG_MUTE - 3) + +/* dynamic range of total level */ +#define TL_STEP 0.75 +#define TL_BITS 6 + +/* dynamic range of sustine level */ +#define SL_STEP 3.0 +#define SL_BITS 4 + +/* damper speed before key-on. key-scale affects. */ +#define DAMPER_RATE 12 + +#define TL2EG(d) ((d) << 1) + +/* sine table */ +#define PG_BITS 10 /* 2^10 = 1024 length sine table */ +#define PG_WIDTH (1 << PG_BITS) + +/* clang-format off */ +/* exp_table[x] = round((exp2((double)x / 256.0) - 1) * 1024) */ +static uint16_t exp_table[256] = { +0, 3, 6, 8, 11, 14, 17, 20, 22, 25, 28, 31, 34, 37, 40, 42, +45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, +93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 130, 133, 136, 139, +142, 145, 148, 152, 155, 158, 161, 164, 168, 171, 174, 177, 181, 184, 187, 190, +194, 197, 200, 204, 207, 210, 214, 217, 220, 224, 227, 231, 234, 237, 241, 244, +248, 251, 255, 258, 262, 265, 268, 272, 276, 279, 283, 286, 290, 293, 297, 300, +304, 308, 311, 315, 318, 322, 326, 329, 333, 337, 340, 344, 348, 352, 355, 359, +363, 367, 370, 374, 378, 382, 385, 389, 393, 397, 401, 405, 409, 412, 416, 420, +424, 428, 432, 436, 440, 444, 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, +488, 492, 496, 501, 505, 509, 513, 517, 521, 526, 530, 534, 538, 542, 547, 551, +555, 560, 564, 568, 572, 577, 581, 585, 590, 594, 599, 603, 607, 612, 616, 621, +625, 630, 634, 639, 643, 648, 652, 657, 661, 666, 670, 675, 680, 684, 689, 693, +698, 703, 708, 712, 717, 722, 726, 731, 736, 741, 745, 750, 755, 760, 765, 770, +774, 779, 784, 789, 794, 799, 804, 809, 814, 819, 824, 829, 834, 839, 844, 849, +854, 859, 864, 869, 874, 880, 885, 890, 895, 900, 906, 911, 916, 921, 927, 932, +937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018 +}; +/* fullsin_table[x] = round(-log2(sin((x + 0.5) * PI / (PG_WIDTH / 4) / 2)) * 256) */ +static uint16_t fullsin_table[PG_WIDTH] = { +2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, +846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, +598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, +453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, +352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, +276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, +215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, +167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, +127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, +94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, +67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, +46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, +29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, +16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, +7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, +2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, +}; +/* clang-format on */ + +static uint16_t halfsin_table[PG_WIDTH]; +static uint16_t *wave_table_map[2] = {fullsin_table, halfsin_table}; + +/* pitch modulator */ +/* offset to fnum, rough approximation of 14 cents depth. */ +static int8_t pm_table[8][8] = { + {0, 0, 0, 0, 0, 0, 0, 0}, // fnum = 000xxxxxx + {0, 0, 1, 0, 0, 0, -1, 0}, // fnum = 001xxxxxx + {0, 1, 2, 1, 0, -1, -2, -1}, // fnum = 010xxxxxx + {0, 1, 3, 1, 0, -1, -3, -1}, // fnum = 011xxxxxx + {0, 2, 4, 2, 0, -2, -4, -2}, // fnum = 100xxxxxx + {0, 2, 5, 2, 0, -2, -5, -2}, // fnum = 101xxxxxx + {0, 3, 6, 3, 0, -3, -6, -3}, // fnum = 110xxxxxx + {0, 3, 7, 3, 0, -3, -7, -3}, // fnum = 111xxxxxx +}; + +/* amplitude lfo table */ +/* The following envelop pattern is verified on real YM2413. */ +/* each element repeates 64 cycles */ +static uint8_t am_table[210] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, // + 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, // + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, // + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, // + 12, 12, 12, 12, 12, 12, 12, 12, // + 13, 13, 13, // + 12, 12, 12, 12, 12, 12, 12, 12, // + 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, // + 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, // + 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, // + 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, // + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; + +/* envelope decay increment step table */ +/* based on andete's research */ +static uint8_t eg_step_tables[4][8] = { + {0, 1, 0, 1, 0, 1, 0, 1}, + {0, 1, 0, 1, 1, 1, 0, 1}, + {0, 1, 1, 1, 0, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1}, +}; + +enum __OPLL_EG_STATE { ATTACK, DECAY, SUSTAIN, RELEASE, DAMP, UNKNOWN }; + +static uint32_t ml_table[16] = {1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, + 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2}; + +#define dB2(x) ((x)*2) +static double kl_table[16] = {dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125), + dB2(16.875), dB2(17.625), dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500), + dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000)}; + +static uint32_t tll_table[8 * 16][1 << TL_BITS][4]; +static int32_t rks_table[8 * 2][2]; + +static OPLL_PATCH null_patch = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2]; + +#define min(i, j) (((i) < (j)) ? (i) : (j)) +#define max(i, j) (((i) > (j)) ? (i) : (j)) + +/*************************************************** + + Internal Sample Rate Converter + +****************************************************/ +/* Note: to disable internal rate converter, set clock/72 to output sampling rate. */ + +/* + * LW is truncate length of sinc(x) calculation. + * Lower LW is faster, higher LW results better quality. + * LW must be a non-zero positive even number, no upper limit. + * LW=16 or greater is recommended when upsampling. + * LW=8 is practically okay for downsampling. + */ +#define LW 16 + +/* resolution of sinc(x) table. sinc(x) where 0.0<=x<1.0 corresponds to sinc_table[0...SINC_RESO-1] */ +#define SINC_RESO 256 +#define SINC_AMP_BITS 12 + +// double hamming(double x) { return 0.54 - 0.46 * cos(2 * PI * x); } +static double blackman(double x) { return 0.42 - 0.5 * cos(2 * _PI_ * x) + 0.08 * cos(4 * _PI_ * x); } +static double sinc(double x) { return (x == 0.0 ? 1.0 : sin(_PI_ * x) / (_PI_ * x)); } +static double windowed_sinc(double x) { return blackman(0.5 + 0.5 * x / (LW / 2)) * sinc(x); } + +/* f_inp: input frequency. f_out: output frequencey, ch: number of channels */ +OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch) { + OPLL_RateConv *conv = malloc(sizeof(OPLL_RateConv)); + int i; + + conv->ch = ch; + conv->f_ratio = f_inp / f_out; + conv->buf = malloc(sizeof(void *) * ch); + for (i = 0; i < ch; i++) { + conv->buf[i] = malloc(sizeof(conv->buf[0][0]) * LW); + } + + /* create sinc_table for positive 0 <= x < LW/2 */ + conv->sinc_table = malloc(sizeof(conv->sinc_table[0]) * SINC_RESO * LW / 2); + for (i = 0; i < SINC_RESO * LW / 2; i++) { + const double x = (double)i / SINC_RESO; + if (f_out < f_inp) { + /* for downsampling */ + conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x / conv->f_ratio) / conv->f_ratio); + } else { + /* for upsampling */ + conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x)); + } + } + + return conv; +} + +static INLINE int16_t lookup_sinc_table(int16_t *table, double x) { + int16_t index = (int16_t)(x * SINC_RESO); + if (index < 0) + index = -index; + return table[min(SINC_RESO * LW / 2 - 1, index)]; +} + +void OPLL_RateConv_reset(OPLL_RateConv *conv) { + int i; + conv->timer = 0; + for (i = 0; i < conv->ch; i++) { + memset(conv->buf[i], 0, sizeof(conv->buf[i][0]) * LW); + } +} + +/* put original data to this converter at f_inp. */ +void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data) { + int16_t *buf = conv->buf[ch]; + int i; + for (i = 0; i < LW - 1; i++) { + buf[i] = buf[i + 1]; + } + buf[LW - 1] = data; +} + +/* get resampled data from this converter at f_out. */ +/* this function must be called f_out / f_inp times per one putData call. */ +int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch) { + int16_t *buf = conv->buf[ch]; + int32_t sum = 0; + int k; + double dn; + conv->timer += conv->f_ratio; + dn = conv->timer - floor(conv->timer); + conv->timer = dn; + + for (k = 0; k < LW; k++) { + double x = ((double)k - (LW / 2 - 1)) - dn; + sum += buf[k] * lookup_sinc_table(conv->sinc_table, x); + } + return sum >> SINC_AMP_BITS; +} + +void OPLL_RateConv_delete(OPLL_RateConv *conv) { + int i; + for (i = 0; i < conv->ch; i++) { + free(conv->buf[i]); + } + free(conv->buf); + free(conv->sinc_table); + free(conv); +} + +/*************************************************** + + Create tables + +****************************************************/ + +static void makeSinTable(void) { + int x; + + for (x = 0; x < PG_WIDTH / 4; x++) { + fullsin_table[PG_WIDTH / 4 + x] = fullsin_table[PG_WIDTH / 4 - x - 1]; + } + + for (x = 0; x < PG_WIDTH / 2; x++) { + fullsin_table[PG_WIDTH / 2 + x] = 0x8000 | fullsin_table[x]; + } + + for (x = 0; x < PG_WIDTH / 2; x++) + halfsin_table[x] = fullsin_table[x]; + + for (x = PG_WIDTH / 2; x < PG_WIDTH; x++) + halfsin_table[x] = 0xfff; +} + +static void makeTllTable(void) { + + int32_t tmp; + int32_t fnum, block, TL, KL; + + for (fnum = 0; fnum < 16; fnum++) { + for (block = 0; block < 8; block++) { + for (TL = 0; TL < 64; TL++) { + for (KL = 0; KL < 4; KL++) { + if (KL == 0) { + tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); + } else { + tmp = (int32_t)(kl_table[fnum] - dB2(3.000) * (7 - block)); + if (tmp <= 0) + tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); + else + tll_table[(block << 4) | fnum][TL][KL] = (uint32_t)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); + } + } + } + } + } +} + +static void makeRksTable(void) { + int fnum8, block; + for (fnum8 = 0; fnum8 < 2; fnum8++) + for (block = 0; block < 8; block++) { + rks_table[(block << 1) | fnum8][1] = (block << 1) + fnum8; + rks_table[(block << 1) | fnum8][0] = block >> 1; + } +} + +static void makeDefaultPatch() { + int i, j; + for (i = 0; i < OPLL_TONE_NUM; i++) + for (j = 0; j < 19; j++) + OPLL_getDefaultPatch(i, j, &default_patch[i][j * 2]); +} + +static uint8_t table_initialized = 0; + +static void initializeTables() { + makeTllTable(); + makeRksTable(); + makeSinTable(); + makeDefaultPatch(); + table_initialized = 1; +} + +/********************************************************* + + Synthesizing + +*********************************************************/ +#define SLOT_BD1 12 +#define SLOT_BD2 13 +#define SLOT_HH 14 +#define SLOT_SD 15 +#define SLOT_TOM 16 +#define SLOT_CYM 17 + +/* utility macros */ +#define MOD(o, x) (&(o)->slot[(x) << 1]) +#define CAR(o, x) (&(o)->slot[((x) << 1) | 1]) +#define BIT(s, b) (((s) >> (b)) & 1) + +#if OPLL_DEBUG +static void _debug_print_patch(OPLL_SLOT *slot) { + OPLL_PATCH *p = slot->patch; + printf("[slot#%d am:%d pm:%d eg:%d kr:%d ml:%d kl:%d tl:%d ws:%d fb:%d A:%d D:%d S:%d R:%d]\n", slot->number, // + p->AM, p->PM, p->EG, p->KR, p->ML, // + p->KL, p->TL, p->WS, p->FB, // + p->AR, p->DR, p->SL, p->RR); +} + +static char *_debug_eg_state_name(OPLL_SLOT *slot) { + switch (slot->eg_state) { + case ATTACK: + return "attack"; + case DECAY: + return "decay"; + case SUSTAIN: + return "sustain"; + case RELEASE: + return "release"; + case DAMP: + return "damp"; + default: + return "unknown"; + } +} + +static INLINE void _debug_print_slot_info(OPLL_SLOT *slot) { + char *name = _debug_eg_state_name(slot); + printf("[slot#%d state:%s fnum:%03x rate:%d-%d]\n", slot->number, name, slot->blk_fnum, slot->eg_rate_h, + slot->eg_rate_l); + _debug_print_patch(slot); + fflush(stdout); +} +#endif + +static INLINE int get_parameter_rate(OPLL_SLOT *slot) { + + if ((slot->type & 1) == 0 && slot->key_flag == 0) { + return 0; + } + + switch (slot->eg_state) { + case ATTACK: + return slot->patch->AR; + case DECAY: + return slot->patch->DR; + case SUSTAIN: + return slot->patch->EG ? 0 : slot->patch->RR; + case RELEASE: + if (slot->sus_flag) { + return 5; + } else if (slot->patch->EG) { + return slot->patch->RR; + } else { + return 7; + } + case DAMP: + return DAMPER_RATE; + default: + return 0; + } +} + +enum SLOT_UPDATE_FLAG { + UPDATE_WS = 1, + UPDATE_TLL = 2, + UPDATE_RKS = 4, + UPDATE_EG = 8, + UPDATE_ALL = 255, +}; + +static INLINE void request_update(OPLL_SLOT *slot, int flag) { slot->update_requests |= flag; } + +static void commit_slot_update(OPLL_SLOT *slot) { + +#if OPLL_DEBUG + if (slot->last_eg_state != slot->eg_state) { + _debug_print_slot_info(slot); + slot->last_eg_state = slot->eg_state; + } +#endif + + if (slot->update_requests & UPDATE_WS) { + slot->wave_table = wave_table_map[slot->patch->WS]; + } + + if (slot->update_requests & UPDATE_TLL) { + if ((slot->type & 1) == 0) { + slot->tll = tll_table[slot->blk_fnum >> 5][slot->patch->TL][slot->patch->KL]; + } else { + slot->tll = tll_table[slot->blk_fnum >> 5][slot->volume][slot->patch->KL]; + } + } + + if (slot->update_requests & UPDATE_RKS) { + slot->rks = rks_table[slot->blk_fnum >> 8][slot->patch->KR]; + } + + if (slot->update_requests & (UPDATE_RKS | UPDATE_EG)) { + int p_rate = get_parameter_rate(slot); + + if (p_rate == 0) { + slot->eg_shift = 0; + slot->eg_rate_h = 0; + slot->eg_rate_l = 0; + return; + } + + slot->eg_rate_h = min(15, p_rate + (slot->rks >> 2)); + slot->eg_rate_l = slot->rks & 3; + if (slot->eg_state == ATTACK) { + slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (13 - slot->eg_rate_h) : 0; + } else { + slot->eg_shift = (slot->eg_rate_h < 13) ? (13 - slot->eg_rate_h) : 0; + } + } + + slot->update_requests = 0; +} + +static void reset_slot(OPLL_SLOT *slot, int number) { + slot->number = number; + slot->type = number % 2; + slot->pg_keep = 0; + slot->wave_table = wave_table_map[0]; + slot->pg_phase = 0; + slot->output[0] = 0; + slot->output[1] = 0; + slot->eg_state = RELEASE; + slot->eg_shift = 0; + slot->rks = 0; + slot->tll = 0; + slot->key_flag = 0; + slot->sus_flag = 0; + slot->blk_fnum = 0; + slot->blk = 0; + slot->fnum = 0; + slot->volume = 0; + slot->pg_out = 0; + slot->eg_out = EG_MUTE; + slot->patch = &null_patch; +} + +static INLINE void slotOn(OPLL *opll, int i) { + OPLL_SLOT *slot = &opll->slot[i]; + slot->key_flag = 1; + slot->eg_state = DAMP; + request_update(slot, UPDATE_EG); +} + +static INLINE void slotOff(OPLL *opll, int i) { + OPLL_SLOT *slot = &opll->slot[i]; + slot->key_flag = 0; + if (slot->type & 1) { + slot->eg_state = RELEASE; + request_update(slot, UPDATE_EG); + } +} + +static INLINE void update_key_status(OPLL *opll) { + const uint8_t r14 = opll->reg[0x0e]; + const uint8_t rhythm_mode = BIT(r14, 5); + uint32_t new_slot_key_status = 0; + uint32_t updated_status; + int ch; + + for (ch = 0; ch < 9; ch++) + if (opll->reg[0x20 + ch] & 0x10) + new_slot_key_status |= 3 << (ch * 2); + + if (rhythm_mode) { + if (r14 & 0x10) + new_slot_key_status |= 3 << SLOT_BD1; + + if (r14 & 0x01) + new_slot_key_status |= 1 << SLOT_HH; + + if (r14 & 0x08) + new_slot_key_status |= 1 << SLOT_SD; + + if (r14 & 0x04) + new_slot_key_status |= 1 << SLOT_TOM; + + if (r14 & 0x02) + new_slot_key_status |= 1 << SLOT_CYM; + } + + updated_status = opll->slot_key_status ^ new_slot_key_status; + + if (updated_status) { + int i; + for (i = 0; i < 18; i++) + if (BIT(updated_status, i)) { + if (BIT(new_slot_key_status, i)) { + slotOn(opll, i); + } else { + slotOff(opll, i); + } + } + } + + opll->slot_key_status = new_slot_key_status; +} + +static INLINE void set_patch(OPLL *opll, int32_t ch, int32_t num) { + opll->patch_number[ch] = num; + MOD(opll, ch)->patch = &opll->patch[num * 2 + 0]; + CAR(opll, ch)->patch = &opll->patch[num * 2 + 1]; + request_update(MOD(opll, ch), UPDATE_ALL); + request_update(CAR(opll, ch), UPDATE_ALL); +} + +static INLINE void set_sus_flag(OPLL *opll, int ch, int flag) { + CAR(opll, ch)->sus_flag = flag; + request_update(CAR(opll, ch), UPDATE_EG); + if (MOD(opll, ch)->type & 1) { + MOD(opll, ch)->sus_flag = flag; + request_update(MOD(opll, ch), UPDATE_EG); + } +} + +/* set volume ( volume : 6bit, register value << 2 ) */ +static INLINE void set_volume(OPLL *opll, int ch, int volume) { + CAR(opll, ch)->volume = volume; + request_update(CAR(opll, ch), UPDATE_TLL); +} + +static INLINE void set_slot_volume(OPLL_SLOT *slot, int volume) { + slot->volume = volume; + request_update(slot, UPDATE_TLL); +} + +/* set f-Nnmber ( fnum : 9bit ) */ +static INLINE void set_fnumber(OPLL *opll, int ch, int fnum) { + OPLL_SLOT *car = CAR(opll, ch); + OPLL_SLOT *mod = MOD(opll, ch); + car->fnum = fnum; + car->blk_fnum = (car->blk_fnum & 0xe00) | (fnum & 0x1ff); + mod->fnum = fnum; + mod->blk_fnum = (mod->blk_fnum & 0xe00) | (fnum & 0x1ff); + request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); + request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); +} + +/* set block data (blk : 3bit ) */ +static INLINE void set_block(OPLL *opll, int ch, int blk) { + OPLL_SLOT *car = CAR(opll, ch); + OPLL_SLOT *mod = MOD(opll, ch); + car->blk = blk; + car->blk_fnum = ((blk & 7) << 9) | (car->blk_fnum & 0x1ff); + mod->blk = blk; + mod->blk_fnum = ((blk & 7) << 9) | (mod->blk_fnum & 0x1ff); + request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); + request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); +} + +static INLINE void update_rhythm_mode(OPLL *opll) { + const uint8_t new_rhythm_mode = (opll->reg[0x0e] >> 5) & 1; + + if (opll->rhythm_mode != new_rhythm_mode) { + + if (new_rhythm_mode) { + opll->slot[SLOT_HH].type = 3; + opll->slot[SLOT_HH].pg_keep = 1; + opll->slot[SLOT_SD].type = 3; + opll->slot[SLOT_TOM].type = 3; + opll->slot[SLOT_CYM].type = 3; + opll->slot[SLOT_CYM].pg_keep = 1; + set_patch(opll, 6, 16); + set_patch(opll, 7, 17); + set_patch(opll, 8, 18); + set_slot_volume(&opll->slot[SLOT_HH], ((opll->reg[0x37] >> 4) & 15) << 2); + set_slot_volume(&opll->slot[SLOT_TOM], ((opll->reg[0x38] >> 4) & 15) << 2); + } else { + opll->slot[SLOT_HH].type = 0; + opll->slot[SLOT_HH].pg_keep = 0; + opll->slot[SLOT_SD].type = 1; + opll->slot[SLOT_TOM].type = 0; + opll->slot[SLOT_CYM].type = 1; + opll->slot[SLOT_CYM].pg_keep = 0; + set_patch(opll, 6, opll->reg[0x36] >> 4); + set_patch(opll, 7, opll->reg[0x37] >> 4); + set_patch(opll, 8, opll->reg[0x38] >> 4); + } + } + + opll->rhythm_mode = new_rhythm_mode; +} + +static void update_ampm(OPLL *opll) { + if (opll->test_flag & 2) { + opll->pm_phase = 0; + opll->am_phase = 0; + } else { + opll->pm_phase += (opll->test_flag & 8) ? 1024 : 1; + opll->am_phase += (opll->test_flag & 8) ? 64 : 1; + } + opll->lfo_am = am_table[(opll->am_phase >> 6) % sizeof(am_table)]; +} + +static void update_noise(OPLL *opll, int cycle) { + int i; + for (i = 0; i < cycle; i++) { + if (opll->noise & 1) { + opll->noise ^= 0x800200; + } + opll->noise >>= 1; + } +} + +static void update_short_noise(OPLL *opll) { + const uint32_t pg_hh = opll->slot[SLOT_HH].pg_out; + const uint32_t pg_cym = opll->slot[SLOT_CYM].pg_out; + + const uint8_t h_bit2 = BIT(pg_hh, PG_BITS - 8); + const uint8_t h_bit7 = BIT(pg_hh, PG_BITS - 3); + const uint8_t h_bit3 = BIT(pg_hh, PG_BITS - 7); + + const uint8_t c_bit3 = BIT(pg_cym, PG_BITS - 7); + const uint8_t c_bit5 = BIT(pg_cym, PG_BITS - 5); + + opll->short_noise = (h_bit2 ^ h_bit7) | (h_bit3 ^ c_bit5) | (c_bit3 ^ c_bit5); +} + +static INLINE void calc_phase(OPLL_SLOT *slot, int32_t pm_phase, uint8_t reset) { + const int8_t pm = slot->patch->PM ? pm_table[(slot->fnum >> 6) & 7][(pm_phase >> 10) & 7] : 0; + if (reset) { + slot->pg_phase = 0; + } + slot->pg_phase += (((slot->fnum & 0x1ff) * 2 + pm) * ml_table[slot->patch->ML]) << slot->blk >> 2; + slot->pg_phase &= (DP_WIDTH - 1); + slot->pg_out = slot->pg_phase >> DP_BASE_BITS; +} + +static INLINE uint8_t lookup_attack_step(OPLL_SLOT *slot, uint32_t counter) { + int index; + + switch (slot->eg_rate_h) { + case 12: + index = (counter & 0xc) >> 1; + return 4 - eg_step_tables[slot->eg_rate_l][index]; + case 13: + index = (counter & 0xc) >> 1; + return 3 - eg_step_tables[slot->eg_rate_l][index]; + case 14: + index = (counter & 0xc) >> 1; + return 2 - eg_step_tables[slot->eg_rate_l][index]; + case 0: + case 15: + return 0; + default: + index = counter >> slot->eg_shift; + return eg_step_tables[slot->eg_rate_l][index & 7] ? 4 : 0; + } +} + +static INLINE uint8_t lookup_decay_step(OPLL_SLOT *slot, uint32_t counter) { + int index; + + switch (slot->eg_rate_h) { + case 0: + return 0; + case 13: + index = ((counter & 0xc) >> 1) | (counter & 1); + return eg_step_tables[slot->eg_rate_l][index]; + case 14: + index = ((counter & 0xc) >> 1); + return eg_step_tables[slot->eg_rate_l][index] + 1; + case 15: + return 2; + default: + index = counter >> slot->eg_shift; + return eg_step_tables[slot->eg_rate_l][index & 7]; + } +} + +static INLINE void start_envelope(OPLL_SLOT *slot) { + if (min(15, slot->patch->AR + (slot->rks >> 2)) == 15) { + slot->eg_state = DECAY; + slot->eg_out = 0; + } else { + slot->eg_state = ATTACK; + slot->eg_out = EG_MUTE; + } + request_update(slot, UPDATE_EG); +} + +static INLINE void calc_envelope(OPLL_SLOT *slot, OPLL_SLOT *buddy, uint16_t eg_counter, uint8_t test) { + + uint32_t mask = (1 << slot->eg_shift) - 1; + uint8_t s; + + if (slot->eg_state == ATTACK) { + if (0 < slot->eg_out && 0 < slot->eg_rate_h && (eg_counter & mask & ~3) == 0) { + s = lookup_attack_step(slot, eg_counter); + if (0 < s) { + slot->eg_out = max(0, ((int)slot->eg_out - (slot->eg_out >> s) - 1)); + } + } + } else { + if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) { + slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter)); + } + } + + switch (slot->eg_state) { + case DAMP: + if (slot->eg_out >= EG_MUTE) { + start_envelope(slot); + if (slot->type & 1) { + if (!slot->pg_keep) { + slot->pg_phase = 0; + } + if (buddy && !buddy->pg_keep) { + buddy->pg_phase = 0; + } + } + } + break; + + case ATTACK: + if (slot->eg_out == 0) { + slot->eg_state = DECAY; + request_update(slot, UPDATE_EG); + } + break; + + case DECAY: + if ((slot->eg_out >> 3) == slot->patch->SL) { + slot->eg_state = SUSTAIN; + request_update(slot, UPDATE_EG); + } + break; + + case SUSTAIN: + case RELEASE: + default: + break; + } + + if (test) { + slot->eg_out = 0; + } +} + +static void update_slots(OPLL *opll) { + int i; + opll->eg_counter++; + + for (i = 0; i < 18; i++) { + OPLL_SLOT *slot = &opll->slot[i]; + OPLL_SLOT *buddy = NULL; + if (slot->type == 0) { + buddy = &opll->slot[i + 1]; + } + if (slot->type == 1) { + buddy = &opll->slot[i - 1]; + } + if (slot->update_requests) { + commit_slot_update(slot); + } + calc_envelope(slot, buddy, opll->eg_counter, opll->test_flag & 1); + calc_phase(slot, opll->pm_phase, opll->test_flag & 4); + } +} + +/* output: -4095...4095 */ +static INLINE int16_t lookup_exp_table(uint16_t i) { + /* from andete's expressoin */ + int16_t t = (exp_table[(i & 0xff) ^ 0xff] + 1024); + int16_t res = t >> ((i & 0x7f00) >> 8); + return ((i & 0x8000) ? ~res : res) << 1; +} + +static INLINE int16_t to_linear(uint16_t h, OPLL_SLOT *slot, int16_t am) { + uint16_t att; + if (slot->eg_out >= EG_MAX) + return 0; + + att = min(EG_MAX, (slot->eg_out + slot->tll + am)) << 4; + return lookup_exp_table(h + att); +} + +static INLINE int16_t calc_slot_car(OPLL *opll, int ch, int16_t fm) { + OPLL_SLOT *slot = CAR(opll, ch); + + uint8_t am = slot->patch->AM ? opll->lfo_am : 0; + + slot->output[1] = slot->output[0]; + slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + 2 * (fm >> 1)) & (PG_WIDTH - 1)], slot, am); + + return slot->output[0]; +} + +static INLINE int16_t calc_slot_mod(OPLL *opll, int ch) { + OPLL_SLOT *slot = MOD(opll, ch); + + int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0; + uint8_t am = slot->patch->AM ? opll->lfo_am : 0; + + slot->output[1] = slot->output[0]; + slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + fm) & (PG_WIDTH - 1)], slot, am); + + return slot->output[0]; +} + +static INLINE int16_t calc_slot_tom(OPLL *opll) { + OPLL_SLOT *slot = MOD(opll, 8); + + return to_linear(slot->wave_table[slot->pg_out], slot, 0); +} + +/* Specify phase offset directly based on 10-bit (1024-length) sine table */ +#define _PD(phase) ((PG_BITS < 10) ? (phase >> (10 - PG_BITS)) : (phase << (PG_BITS - 10))) + +static INLINE int16_t calc_slot_snare(OPLL *opll) { + OPLL_SLOT *slot = CAR(opll, 7); + + uint32_t phase; + + if (BIT(slot->pg_out, PG_BITS - 2)) + phase = (opll->noise & 1) ? _PD(0x300) : _PD(0x200); + else + phase = (opll->noise & 1) ? _PD(0x0) : _PD(0x100); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +static INLINE int16_t calc_slot_cym(OPLL *opll) { + OPLL_SLOT *slot = CAR(opll, 8); + + uint32_t phase = opll->short_noise ? _PD(0x300) : _PD(0x100); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +static INLINE int16_t calc_slot_hat(OPLL *opll) { + OPLL_SLOT *slot = MOD(opll, 7); + + uint32_t phase; + + if (opll->short_noise) + phase = (opll->noise & 1) ? _PD(0x2d0) : _PD(0x234); + else + phase = (opll->noise & 1) ? _PD(0x34) : _PD(0xd0); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +#define _MO(x) (-(x) >> 1) +#define _RO(x) (x) + +static void update_output(OPLL *opll) { + int16_t *out; + int i; + + update_ampm(opll); + update_short_noise(opll); + update_slots(opll); + + out = opll->ch_out; + + /* CH1-6 */ + for (i = 0; i < 6; i++) { + if (!(opll->mask & OPLL_MASK_CH(i))) { + out[i] = _MO(calc_slot_car(opll, i, calc_slot_mod(opll, i))); + } + } + + /* CH7 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(6))) { + out[6] = _MO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); + } + } else { + if (!(opll->mask & OPLL_MASK_BD)) { + out[9] = _RO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); + } + } + update_noise(opll, 14); + + /* CH8 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(7))) { + out[7] = _MO(calc_slot_car(opll, 7, calc_slot_mod(opll, 7))); + } + } else { + if (!(opll->mask & OPLL_MASK_HH)) { + out[10] = _RO(calc_slot_hat(opll)); + } + if (!(opll->mask & OPLL_MASK_SD)) { + out[11] = _RO(calc_slot_snare(opll)); + } + } + update_noise(opll, 2); + + /* CH9 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(8))) { + out[8] = _MO(calc_slot_car(opll, 8, calc_slot_mod(opll, 8))); + } + } else { + if (!(opll->mask & OPLL_MASK_TOM)) { + out[12] = _RO(calc_slot_tom(opll)); + } + if (!(opll->mask & OPLL_MASK_CYM)) { + out[13] = _RO(calc_slot_cym(opll)); + } + } + update_noise(opll, 2); +} + +INLINE static void mix_output(OPLL *opll) { + int16_t out = 0; + int i; + for (i = 0; i < 14; i++) { + out += opll->ch_out[i]; + } + if (opll->conv) { + OPLL_RateConv_putData(opll->conv, 0, out); + } else { + opll->mix_out[0] = out; + } +} + +INLINE static void mix_output_stereo(OPLL *opll) { + int16_t *out = opll->mix_out; + int i; + out[0] = out[1] = 0; + for (i = 0; i < 14; i++) { + if (opll->pan[i] & 2) + out[0] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][0]); + if (opll->pan[i] & 1) + out[1] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][1]); + } + if (opll->conv) { + OPLL_RateConv_putData(opll->conv, 0, out[0]); + OPLL_RateConv_putData(opll->conv, 1, out[1]); + } +} + +/*********************************************************** + + External Interfaces + +***********************************************************/ + +OPLL *OPLL_new(uint32_t clk, uint32_t rate) { + OPLL *opll; + int i; + + if (!table_initialized) { + initializeTables(); + } + + opll = (OPLL *)calloc(sizeof(OPLL), 1); + if (opll == NULL) + return NULL; + + for (i = 0; i < 19 * 2; i++) + memcpy(&opll->patch[i], &null_patch, sizeof(OPLL_PATCH)); + + opll->clk = clk; + opll->rate = rate; + opll->mask = 0; + opll->conv = NULL; + opll->mix_out[0] = 0; + opll->mix_out[1] = 0; + + OPLL_reset(opll); + OPLL_setChipType(opll, 0); + OPLL_resetPatch(opll, 0); + return opll; +} + +void OPLL_delete(OPLL *opll) { + if (opll->conv) { + OPLL_RateConv_delete(opll->conv); + opll->conv = NULL; + } + free(opll); +} + +static void reset_rate_conversion_params(OPLL *opll) { + const double f_out = opll->rate; + const double f_inp = opll->clk / 72; + + opll->out_time = 0; + opll->out_step = ((uint32_t)f_inp) << 8; + opll->inp_step = ((uint32_t)f_out) << 8; + + if (opll->conv) { + OPLL_RateConv_delete(opll->conv); + opll->conv = NULL; + } + + if (floor(f_inp) != f_out && floor(f_inp + 0.5) != f_out) { + opll->conv = OPLL_RateConv_new(f_inp, f_out, 2); + } + + if (opll->conv) { + OPLL_RateConv_reset(opll->conv); + } +} + +void OPLL_reset(OPLL *opll) { + int i; + + if (!opll) + return; + + opll->adr = 0; + + opll->pm_phase = 0; + opll->am_phase = 0; + + opll->noise = 0x1; + opll->mask = 0; + + opll->rhythm_mode = 0; + opll->slot_key_status = 0; + opll->eg_counter = 0; + + reset_rate_conversion_params(opll); + + for (i = 0; i < 18; i++) + reset_slot(&opll->slot[i], i); + + for (i = 0; i < 9; i++) { + set_patch(opll, i, 0); + } + + for (i = 0; i < 0x40; i++) + OPLL_writeReg(opll, i, 0); + + for (i = 0; i < 15; i++) { + opll->pan[i] = 3; + opll->pan_fine[i][1] = opll->pan_fine[i][0] = 1.0f; + } + + for (i = 0; i < 14; i++) { + opll->ch_out[i] = 0; + } +} + +void OPLL_forceRefresh(OPLL *opll) { + int i; + + if (opll == NULL) + return; + + for (i = 0; i < 9; i++) { + set_patch(opll, i, opll->patch_number[i]); + } + + for (i = 0; i < 18; i++) { + request_update(&opll->slot[i], UPDATE_ALL); + } +} + +void OPLL_setRate(OPLL *opll, uint32_t rate) { + opll->rate = rate; + reset_rate_conversion_params(opll); +} + +void OPLL_setQuality(OPLL *opll, uint8_t q) {} + +void OPLL_setChipType(OPLL *opll, uint8_t type) { opll->chip_type = type; } + +void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t data) { + int ch, i; + + if (reg >= 0x40) + return; + + /* mirror registers */ + if ((0x19 <= reg && reg <= 0x1f) || (0x29 <= reg && reg <= 0x2f) || (0x39 <= reg && reg <= 0x3f)) { + reg -= 9; + } + + opll->reg[reg] = (uint8_t)data; + + switch (reg) { + case 0x00: + opll->patch[0].AM = (data >> 7) & 1; + opll->patch[0].PM = (data >> 6) & 1; + opll->patch[0].EG = (data >> 5) & 1; + opll->patch[0].KR = (data >> 4) & 1; + opll->patch[0].ML = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_RKS | UPDATE_EG); + } + } + break; + + case 0x01: + opll->patch[1].AM = (data >> 7) & 1; + opll->patch[1].PM = (data >> 6) & 1; + opll->patch[1].EG = (data >> 5) & 1; + opll->patch[1].KR = (data >> 4) & 1; + opll->patch[1].ML = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_RKS | UPDATE_EG); + } + } + break; + + case 0x02: + opll->patch[0].KL = (data >> 6) & 3; + opll->patch[0].TL = (data)&63; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_TLL); + } + } + break; + + case 0x03: + opll->patch[1].KL = (data >> 6) & 3; + opll->patch[1].WS = (data >> 4) & 1; + opll->patch[0].WS = (data >> 3) & 1; + opll->patch[0].FB = (data)&7; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_WS); + request_update(CAR(opll, i), UPDATE_WS | UPDATE_TLL); + } + } + break; + + case 0x04: + opll->patch[0].AR = (data >> 4) & 15; + opll->patch[0].DR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_EG); + } + } + break; + + case 0x05: + opll->patch[1].AR = (data >> 4) & 15; + opll->patch[1].DR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_EG); + } + } + break; + + case 0x06: + opll->patch[0].SL = (data >> 4) & 15; + opll->patch[0].RR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_EG); + } + } + break; + + case 0x07: + opll->patch[1].SL = (data >> 4) & 15; + opll->patch[1].RR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_EG); + } + } + break; + + case 0x0e: + if (opll->chip_type == 1) + break; + update_rhythm_mode(opll); + update_key_status(opll); + break; + + case 0x0f: + opll->test_flag = data; + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + ch = reg - 0x10; + set_fnumber(opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8)); + break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + ch = reg - 0x20; + set_fnumber(opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]); + set_block(opll, ch, (data >> 1) & 7); + set_sus_flag(opll, ch, (data >> 5) & 1); + update_key_status(opll); + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + if ((opll->reg[0x0e] & 32) && (reg >= 0x36)) { + switch (reg) { + case 0x37: + set_slot_volume(MOD(opll, 7), ((data >> 4) & 15) << 2); + break; + case 0x38: + set_slot_volume(MOD(opll, 8), ((data >> 4) & 15) << 2); + break; + default: + break; + } + } else { + set_patch(opll, reg - 0x30, (data >> 4) & 15); + } + set_volume(opll, reg - 0x30, (data & 15) << 2); + break; + + default: + break; + } +} + +void OPLL_writeIO(OPLL *opll, uint32_t adr, uint8_t val) { + if (adr & 1) + OPLL_writeReg(opll, opll->adr, val); + else + opll->adr = val; +} + +void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan) { opll->pan[ch & 15] = pan; } + +void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]) { + opll->pan_fine[ch & 15][0] = pan[0]; + opll->pan_fine[ch & 15][1] = pan[1]; +} + +void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch) { + patch[0].AM = (dump[0] >> 7) & 1; + patch[1].AM = (dump[1] >> 7) & 1; + patch[0].PM = (dump[0] >> 6) & 1; + patch[1].PM = (dump[1] >> 6) & 1; + patch[0].EG = (dump[0] >> 5) & 1; + patch[1].EG = (dump[1] >> 5) & 1; + patch[0].KR = (dump[0] >> 4) & 1; + patch[1].KR = (dump[1] >> 4) & 1; + patch[0].ML = (dump[0]) & 15; + patch[1].ML = (dump[1]) & 15; + patch[0].KL = (dump[2] >> 6) & 3; + patch[1].KL = (dump[3] >> 6) & 3; + patch[0].TL = (dump[2]) & 63; + patch[1].TL = 0; + patch[0].FB = (dump[3]) & 7; + patch[1].FB = 0; + patch[0].WS = (dump[3] >> 3) & 1; + patch[1].WS = (dump[3] >> 4) & 1; + patch[0].AR = (dump[4] >> 4) & 15; + patch[1].AR = (dump[5] >> 4) & 15; + patch[0].DR = (dump[4]) & 15; + patch[1].DR = (dump[5]) & 15; + patch[0].SL = (dump[6] >> 4) & 15; + patch[1].SL = (dump[7] >> 4) & 15; + patch[0].RR = (dump[6]) & 15; + patch[1].RR = (dump[7]) & 15; +} + +void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *patch) { + OPLL_dump2patch(default_inst[type] + num * 8, patch); +} + +void OPLL_setPatch(OPLL *opll, const uint8_t *dump) { + OPLL_PATCH patch[2]; + int i; + for (i = 0; i < 19; i++) { + OPLL_dump2patch(dump + i * 8, patch); + memcpy(&opll->patch[i * 2 + 0], &patch[0], sizeof(OPLL_PATCH)); + memcpy(&opll->patch[i * 2 + 1], &patch[1], sizeof(OPLL_PATCH)); + } +} + +void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump) { + dump[0] = (uint8_t)((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML); + dump[1] = (uint8_t)((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML); + dump[2] = (uint8_t)((patch[0].KL << 6) + patch[0].TL); + dump[3] = (uint8_t)((patch[1].KL << 6) + (patch[1].WS << 4) + (patch[0].WS << 3) + patch[0].FB); + dump[4] = (uint8_t)((patch[0].AR << 4) + patch[0].DR); + dump[5] = (uint8_t)((patch[1].AR << 4) + patch[1].DR); + dump[6] = (uint8_t)((patch[0].SL << 4) + patch[0].RR); + dump[7] = (uint8_t)((patch[1].SL << 4) + patch[1].RR); +} + +void OPLL_copyPatch(OPLL *opll, int32_t num, OPLL_PATCH *patch) { + memcpy(&opll->patch[num], patch, sizeof(OPLL_PATCH)); +} + +void OPLL_resetPatch(OPLL *opll, uint8_t type) { + int i; + for (i = 0; i < 19 * 2; i++) + OPLL_copyPatch(opll, i, &default_patch[type % OPLL_TONE_NUM][i]); +} + +int16_t OPLL_calc(OPLL *opll) { + while (opll->out_step > opll->out_time) { + opll->out_time += opll->inp_step; + update_output(opll); + mix_output(opll); + } + opll->out_time -= opll->out_step; + if (opll->conv) { + opll->mix_out[0] = OPLL_RateConv_getData(opll->conv, 0); + } + return opll->mix_out[0]; +} + +void OPLL_calcStereo(OPLL *opll, int32_t out[2]) { + while (opll->out_step > opll->out_time) { + opll->out_time += opll->inp_step; + update_output(opll); + mix_output_stereo(opll); + } + opll->out_time -= opll->out_step; + if (opll->conv) { + out[0] = OPLL_RateConv_getData(opll->conv, 0); + out[1] = OPLL_RateConv_getData(opll->conv, 1); + } else { + out[0] = opll->mix_out[0]; + out[1] = opll->mix_out[1]; + } +} + +uint32_t OPLL_setMask(OPLL *opll, uint32_t mask) { + uint32_t ret; + + if (opll) { + ret = opll->mask; + opll->mask = mask; + return ret; + } else + return 0; +} + +uint32_t OPLL_toggleMask(OPLL *opll, uint32_t mask) { + uint32_t ret; + + if (opll) { + ret = opll->mask; + opll->mask ^= mask; + return ret; + } else + return 0; +} diff --git a/extern/NSFplay/legacy/emu2413.h b/extern/NSFplay/legacy/emu2413.h new file mode 100644 index 000000000..2804f3fac --- /dev/null +++ b/extern/NSFplay/legacy/emu2413.h @@ -0,0 +1,247 @@ +#ifndef _EMU2413_H_ +#define _EMU2413_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPLL_DEBUG 0 + +enum OPLL_TONE_ENUM { + OPLL_VRC7_TONE = 0, + OPLL_VRC7_RW_TONE, + OPLL_VRC7_FT36_TONE, + OPLL_VRC7_FT35_TONE, + OPLL_VRC7_MO_TONE, + OPLL_VRC7_KT2_TONE, + OPLL_VRC7_KT1_TONE, + OPLL_2413_TONE, + OPLL_281B_TONE +}; + +/* voice data */ +typedef struct __OPLL_PATCH { + uint32_t TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WS; +} OPLL_PATCH; + +/* slot */ +typedef struct __OPLL_SLOT { + uint8_t number; + + /* type flags: + * 000000SM + * |+-- M: 0:modulator 1:carrier + * +--- S: 0:normal 1:single slot mode (sd, tom, hh or cym) + */ + uint8_t type; + + OPLL_PATCH *patch; /* voice parameter */ + + /* slot output */ + int32_t output[2]; /* output value, latest and previous. */ + + /* phase generator (pg) */ + uint16_t *wave_table; /* wave table */ + uint32_t pg_phase; /* pg phase */ + uint32_t pg_out; /* pg output, as index of wave table */ + uint8_t pg_keep; /* if 1, pg_phase is preserved when key-on */ + uint16_t blk_fnum; /* (block << 9) | f-number */ + uint16_t fnum; /* f-number (9 bits) */ + uint8_t blk; /* block (3 bits) */ + + /* envelope generator (eg) */ + uint8_t eg_state; /* current state */ + int32_t volume; /* current volume */ + uint8_t key_flag; /* key-on flag 1:on 0:off */ + uint8_t sus_flag; /* key-sus option 1:on 0:off */ + uint16_t tll; /* total level + key scale level*/ + uint8_t rks; /* key scale offset (rks) for eg speed */ + uint8_t eg_rate_h; /* eg speed rate high 4bits */ + uint8_t eg_rate_l; /* eg speed rate low 2bits */ + uint32_t eg_shift; /* shift for eg global counter, controls envelope speed */ + uint32_t eg_out; /* eg output */ + + uint32_t update_requests; /* flags to debounce update */ + +#if OPLL_DEBUG + uint8_t last_eg_state; +#endif +} OPLL_SLOT; + +/* mask */ +#define OPLL_MASK_CH(x) (1 << (x)) +#define OPLL_MASK_HH (1 << (9)) +#define OPLL_MASK_CYM (1 << (10)) +#define OPLL_MASK_TOM (1 << (11)) +#define OPLL_MASK_SD (1 << (12)) +#define OPLL_MASK_BD (1 << (13)) +#define OPLL_MASK_RHYTHM (OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD) + +/* rate conveter */ +typedef struct __OPLL_RateConv { + int ch; + double timer; + double f_ratio; + int16_t *sinc_table; + int16_t **buf; +} OPLL_RateConv; + +OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch); +void OPLL_RateConv_reset(OPLL_RateConv *conv); +void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data); +int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch); +void OPLL_RateConv_delete(OPLL_RateConv *conv); + +typedef struct __OPLL { + uint32_t clk; + uint32_t rate; + + uint8_t chip_type; + + uint32_t adr; + + uint32_t inp_step; + uint32_t out_step; + uint32_t out_time; + + uint8_t reg[0x40]; + uint8_t test_flag; + uint32_t slot_key_status; + uint8_t rhythm_mode; + + uint32_t eg_counter; + + uint32_t pm_phase; + int32_t am_phase; + + uint8_t lfo_am; + + uint32_t noise; + uint8_t short_noise; + + int32_t patch_number[9]; + OPLL_SLOT slot[18]; + OPLL_PATCH patch[19 * 2]; + + uint8_t pan[16]; + float pan_fine[16][2]; + + uint32_t mask; + + /* channel output */ + /* 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym */ + int16_t ch_out[14]; + + int16_t mix_out[2]; + + OPLL_RateConv *conv; +} OPLL; + +OPLL *OPLL_new(uint32_t clk, uint32_t rate); +void OPLL_delete(OPLL *); + +void OPLL_reset(OPLL *); +void OPLL_resetPatch(OPLL *, uint8_t); + +/** + * Set output wave sampling rate. + * @param rate sampling rate. If clock / 72 (typically 49716 or 49715 at 3.58MHz) is set, the internal rate converter is + * disabled. + */ +void OPLL_setRate(OPLL *opll, uint32_t rate); + +/** + * Set internal calcuration quality. Currently no effects, just for compatibility. + * >= v1.0.0 always synthesizes internal output at clock/72 Hz. + */ +void OPLL_setQuality(OPLL *opll, uint8_t q); + +/** + * Set pan pot (extra function - not YM2413 chip feature) + * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved + * @param pan 0:mute 1:right 2:left 3:center + * ``` + * pan: 76543210 + * |+- bit 1: enable Left output + * +-- bit 0: enable Right output + * ``` + */ +void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan); + +/** + * Set fine-grained panning + * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved + * @param pan output strength of left/right channel. + * pan[0]: left, pan[1]: right. pan[0]=pan[1]=1.0f for center. + */ +void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]); + +/** + * Set chip type. If vrc7 is selected, r#14 is ignored. + * This method not change the current ROM patch set. + * To change ROM patch set, use OPLL_resetPatch. + * @param type 0:YM2413 1:VRC7 + */ +void OPLL_setChipType(OPLL *opll, uint8_t type); + +void OPLL_writeIO(OPLL *opll, uint32_t reg, uint8_t val); +void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t val); + +/** + * Calculate one sample + */ +int16_t OPLL_calc(OPLL *opll); + +/** + * Calulate stereo sample + */ +void OPLL_calcStereo(OPLL *opll, int32_t out[2]); + +void OPLL_setPatch(OPLL *, const uint8_t *dump); +void OPLL_copyPatch(OPLL *, int32_t, OPLL_PATCH *); + +/** + * Force to refresh. + * External program should call this function after updating patch parameters. + */ +void OPLL_forceRefresh(OPLL *); + +void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch); +void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump); +void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *); + +/** + * Set channel mask + * @param mask mask flag: OPLL_MASK_* can be used. + * - bit 0..8: mask for ch 1 to 9 (OPLL_MASK_CH(i)) + * - bit 9: mask for Hi-Hat (OPLL_MASK_HH) + * - bit 10: mask for Top-Cym (OPLL_MASK_CYM) + * - bit 11: mask for Tom (OPLL_MASK_TOM) + * - bit 12: mask for Snare Drum (OPLL_MASK_SD) + * - bit 13: mask for Bass Drum (OPLL_MASK_BD) + */ +uint32_t OPLL_setMask(OPLL *, uint32_t mask); + +/** + * Toggler channel mask flag + */ +uint32_t OPLL_toggleMask(OPLL *, uint32_t mask); + +/* for compatibility */ +#define OPLL_set_rate OPLL_setRate +#define OPLL_set_quality OPLL_setQuality +#define OPLL_set_pan OPLL_setPan +#define OPLL_set_pan_fine OPLL_setPanFine +#define OPLL_calc_stereo OPLL_calcStereo +#define OPLL_reset_patch OPLL_resetPatch +#define OPLL_dump2patch OPLL_dumpToPatch +#define OPLL_patch2dump OPLL_patchToDump +#define OPLL_setChipMode OPLL_setChipType + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emutypes.h b/extern/NSFplay/legacy/emutypes.h new file mode 100644 index 000000000..bf5d7e1bf --- /dev/null +++ b/extern/NSFplay/legacy/emutypes.h @@ -0,0 +1,41 @@ +#ifndef _EMUTYPES_H_ +#define _EMUTYPES_H_ + +#if defined(_MSC_VER) +#define INLINE __forceinline +#elif defined(__GNUC__) +#define INLINE __inline__ +#elif defined(_MWERKS_) +#define INLINE inline +#else +#define INLINE +#endif + +#if defined(EMU_DLL_IMPORTS) +#define EMU2149_DLL_IMPORTS +#define EMU2212_DLL_IMPORTS +#define EMU2413_DLL_IMPORTS +#define EMU8950_DLL_IMPORTS +#define EMU76489_DLL_IMPORTS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int e_uint; +typedef signed int e_int; + +typedef unsigned char e_uint8 ; +typedef signed char e_int8 ; + +typedef unsigned short e_uint16 ; +typedef signed short e_int16 ; + +typedef unsigned int e_uint32 ; +typedef signed int e_int32 ; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/extern/NSFplay/legacy/vrc7tone_ft35.h b/extern/NSFplay/legacy/vrc7tone_ft35.h new file mode 100644 index 000000000..7af08bcf7 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_ft35.h @@ -0,0 +1,20 @@ +// patch set by Mitsutaka Okazaki used in FamiTracker 0.3.5 and prior (6/24/2001) +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x33, 0x01, 0x09, 0x0e, 0x94, 0x90, 0x40, 0x01, +0x13, 0x41, 0x0f, 0x0d, 0xce, 0xd3, 0x43, 0x13, +0x01, 0x12, 0x1b, 0x06, 0xff, 0xd2, 0x00, 0x32, +0x61, 0x61, 0x1b, 0x07, 0xaf, 0x63, 0x20, 0x28, +0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x08, 0x28, +0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xf8, +0x21, 0x61, 0x1c, 0x07, 0x82, 0x81, 0x10, 0x17, +0x23, 0x21, 0x20, 0x1f, 0xc0, 0x71, 0x07, 0x47, +0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xf8, +0x17, 0x21, 0x28, 0x07, 0xff, 0x83, 0x02, 0xf8, +0x97, 0x81, 0x25, 0x07, 0xcf, 0xc8, 0x02, 0x14, +0x21, 0x21, 0x54, 0x0f, 0x80, 0x7f, 0x07, 0x07, +0x01, 0x01, 0x56, 0x03, 0xd3, 0xb2, 0x43, 0x58, +0x31, 0x21, 0x0c, 0x03, 0x82, 0xc0, 0x40, 0x07, +0x21, 0x01, 0x0c, 0x03, 0xd4, 0xd3, 0x40, 0x84, +0x04, 0x21, 0x28, 0x00, 0xdf, 0xf8, 0xff, 0xf8, +0x23, 0x22, 0x00, 0x00, 0xa8, 0xf8, 0xf8, 0xf8, +0x25, 0x18, 0x00, 0x00, 0xf8, 0xa9, 0xf8, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_ft36.h b/extern/NSFplay/legacy/vrc7tone_ft36.h new file mode 100644 index 000000000..f693955e6 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_ft36.h @@ -0,0 +1,21 @@ +// patch set by quietust (1/18/2004), used in FamiTracker 0.3.6 +// Source: http://nesdev.com/cgi-bin/wwwthreads/showpost.pl?Board=NESemdev&Number=1440 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17, +0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12, +0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02, +0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27, +0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, +0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95, +0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07, +0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15, +0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11, +0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12, +0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4, +0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16, +0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51, +0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05, +0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt1.h b/extern/NSFplay/legacy/vrc7tone_kt1.h new file mode 100644 index 000000000..59cff8a3a --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_kt1.h @@ -0,0 +1,21 @@ +// patch set 1 by kevtris (11/14/1999) +// http://kevtris.org/nes/vrcvii.txt +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x05, 0x03, 0x10, 0x06, 0x74, 0xA1, 0x13, 0xF4, +0x05, 0x01, 0x16, 0x00, 0xF9, 0xA2, 0x15, 0xF5, +0x01, 0x41, 0x11, 0x00, 0xA0, 0xA0, 0x83, 0x95, +0x01, 0x41, 0x17, 0x00, 0x60, 0xF0, 0x83, 0x95, +0x24, 0x41, 0x1F, 0x00, 0x50, 0xB0, 0x94, 0x94, +0x05, 0x01, 0x0B, 0x04, 0x65, 0xA0, 0x54, 0x95, +0x11, 0x41, 0x0E, 0x04, 0x70, 0xC7, 0x13, 0x10, +0x02, 0x44, 0x16, 0x06, 0xE0, 0xE0, 0x31, 0x35, +0x48, 0x22, 0x22, 0x07, 0x50, 0xA1, 0xA5, 0xF4, +0x05, 0xA1, 0x18, 0x00, 0xA2, 0xA2, 0xF5, 0xF5, +0x07, 0x81, 0x2B, 0x05, 0xA5, 0xA5, 0x03, 0x03, +0x01, 0x41, 0x08, 0x08, 0xA0, 0xA0, 0x83, 0x95, +0x21, 0x61, 0x12, 0x00, 0x93, 0x92, 0x74, 0x75, +0x21, 0x62, 0x21, 0x00, 0x84, 0x85, 0x34, 0x15, +0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x34, 0x15, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt2.h b/extern/NSFplay/legacy/vrc7tone_kt2.h new file mode 100644 index 000000000..92caa42bf --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_kt2.h @@ -0,0 +1,21 @@ +// patch set 2 by kevtris (11/15/1999) +// http://kevtris.org/nes/vrcvii.txt +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x31, 0x22, 0x23, 0x07, 0xF0, 0xF0, 0xE8, 0xF7, +0x03, 0x31, 0x68, 0x05, 0xF2, 0x74, 0x79, 0x9C, +0x01, 0x51, 0x72, 0x04, 0xF1, 0xD3, 0x9D, 0x8B, +0x22, 0x61, 0x1B, 0x05, 0xC0, 0xA1, 0xF8, 0xE8, +0x22, 0x61, 0x2C, 0x03, 0xD2, 0xA1, 0xA7, 0xE8, +0x31, 0x22, 0xFA, 0x01, 0xF1, 0xF1, 0xF4, 0xEE, +0x21, 0x61, 0x28, 0x06, 0xF1, 0xF1, 0xCE, 0x9B, +0x27, 0x61, 0x60, 0x00, 0xF0, 0xF0, 0xFF, 0xFD, +0x60, 0x21, 0x2B, 0x06, 0x85, 0xF1, 0x79, 0x9D, +0x31, 0xA1, 0xFF, 0x0A, 0x53, 0x62, 0x5E, 0xAF, +0x03, 0xA1, 0x70, 0x0F, 0xD4, 0xA3, 0x94, 0xBE, +0x2B, 0x61, 0xE4, 0x07, 0xF6, 0x93, 0xBD, 0xAC, +0x21, 0x63, 0xED, 0x07, 0x77, 0xF1, 0xC7, 0xE8, +0x21, 0x61, 0x2A, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, +0x21, 0x63, 0x37, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_mo.h b/extern/NSFplay/legacy/vrc7tone_mo.h new file mode 100644 index 000000000..3b216f874 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_mo.h @@ -0,0 +1,20 @@ +/* VRC7 TONES by okazaki@angel.ne.jp (4/10/2004) */ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x33,0x01,0x09,0x0e,0x94,0x90,0x40,0x01, +0x13,0x41,0x0f,0x0d,0xce,0xd3,0x43,0x13, +0x01,0x12,0x1b,0x06,0xff,0xd2,0x00,0x32, +0x61,0x61,0x1b,0x07,0xaf,0x63,0x20,0x28, +0x22,0x21,0x1e,0x06,0xf0,0x76,0x08,0x28, +0x66,0x21,0x15,0x00,0x93,0x94,0x20,0xf8, +0x21,0x61,0x1c,0x07,0x82,0x81,0x10,0x17, +0x23,0x21,0x20,0x1f,0xc0,0x71,0x07,0x47, +0x25,0x31,0x26,0x05,0x64,0x41,0x18,0xf8, +0x17,0x21,0x28,0x07,0xff,0x83,0x02,0xf8, +0x97,0x81,0x25,0x07,0xcf,0xc8,0x02,0x14, +0x21,0x21,0x54,0x0f,0x80,0x7f,0x07,0x07, +0x01,0x01,0x56,0x03,0xd3,0xb2,0x43,0x58, +0x31,0x21,0x0c,0x03,0x82,0xc0,0x40,0x07, +0x21,0x01,0x0c,0x03,0xd4,0xd3,0x40,0x84, +0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, +0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_nuke.h b/extern/NSFplay/legacy/vrc7tone_nuke.h new file mode 100644 index 000000000..0afcc4ce0 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_nuke.h @@ -0,0 +1,21 @@ +// patch set by Nuke.YKT (3/15/2019) +// https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#ym2413_instruments +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27, +0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12, +0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12, +0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27, +0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28, +0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4, +0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07, +0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17, +0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01, +0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02, +0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12, +0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16, +0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02, +0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6, +0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06, +0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, +0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x68, +0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_rw.h b/extern/NSFplay/legacy/vrc7tone_rw.h new file mode 100644 index 000000000..bf2261208 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_rw.h @@ -0,0 +1,21 @@ +// patch set by rainwarrior (8/01/2012) +// http://forums.nesdev.com/viewtopic.php?f=6&t=9141 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x05, 0x06, 0xB8, 0x82, 0x42, 0x27, +0x13, 0x41, 0x13, 0x0D, 0xD8, 0xD6, 0x23, 0x12, +0x31, 0x11, 0x08, 0x08, 0xFA, 0x9A, 0x22, 0x02, +0x31, 0x61, 0x18, 0x07, 0x78, 0x64, 0x30, 0x27, +0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, +0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0xF5, +0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x16, 0x07, +0x23, 0x21, 0x1A, 0x17, 0xCF, 0x72, 0x25, 0x17, +0x15, 0x11, 0x25, 0x00, 0x4F, 0x71, 0x00, 0x11, +0x85, 0x01, 0x12, 0x0F, 0x99, 0xA2, 0x40, 0x02, +0x07, 0xC1, 0x69, 0x07, 0xF3, 0xF5, 0xA7, 0x12, +0x71, 0x23, 0x0D, 0x06, 0x66, 0x75, 0x23, 0x16, +0x01, 0x02, 0xD3, 0x05, 0xA3, 0x92, 0xF7, 0x52, +0x61, 0x63, 0x0C, 0x00, 0x94, 0xAF, 0x34, 0x06, +0x21, 0x62, 0x0D, 0x00, 0xB1, 0xA0, 0x54, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/nes_apu.cpp b/extern/NSFplay/nes_apu.cpp new file mode 100644 index 000000000..9d50dfc78 --- /dev/null +++ b/extern/NSFplay/nes_apu.cpp @@ -0,0 +1,400 @@ +// +// NES 2A03 +// +#include +#include "nes_apu.h" + +namespace xgm +{ + void NES_APU::sweep_sqr (int i) + { + int shifted = freq[i] >> sweep_amount[i]; + if (i == 0 && sweep_mode[i]) shifted += 1; + sfreq[i] = freq[i] + (sweep_mode[i] ? -shifted : shifted); + //DEBUG_OUT("shifted[%d] = %d (%d >> %d)\n",i,shifted,freq[i],sweep_amount[i]); + } + + void NES_APU::FrameSequence(int s) + { + //DEBUG_OUT("FrameSequence(%d)\n",s); + + if (s > 3) return; // no operation in step 4 + + // 240hz clock + for (int i=0; i < 2; ++i) + { + bool divider = false; + if (envelope_write[i]) + { + envelope_write[i] = false; + envelope_counter[i] = 15; + envelope_div[i] = 0; + } + else + { + ++envelope_div[i]; + if (envelope_div[i] > envelope_div_period[i]) + { + divider = true; + envelope_div[i] = 0; + } + } + if (divider) + { + if (envelope_loop[i] && envelope_counter[i] == 0) + envelope_counter[i] = 15; + else if (envelope_counter[i] > 0) + --envelope_counter[i]; + } + } + + // 120hz clock + if ((s&1) == 0) + for (int i=0; i < 2; ++i) + { + if (!envelope_loop[i] && (length_counter[i] > 0)) + --length_counter[i]; + + if (sweep_enable[i]) + { + //DEBUG_OUT("Clock sweep: %d\n", i); + + --sweep_div[i]; + if (sweep_div[i] <= 0) + { + sweep_sqr(i); // calculate new sweep target + + //DEBUG_OUT("sweep_div[%d] (0/%d)\n",i,sweep_div_period[i]); + //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%d\n",i,freq[i],i,sfreq[i]); + + if (freq[i] >= 8 && sfreq[i] < 0x800 && sweep_amount[i] > 0) // update frequency if appropriate + { + freq[i] = sfreq[i] < 0 ? 0 : sfreq[i]; + } + sweep_div[i] = sweep_div_period[i] + 1; + + //DEBUG_OUT("freq[%d]=%d\n",i,freq[i]); + } + + if (sweep_write[i]) + { + sweep_div[i] = sweep_div_period[i] + 1; + sweep_write[i] = false; + } + } + } + + } + + INT32 NES_APU::calc_sqr (int i, UINT32 clocks) + { + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + scounter[i] -= clocks; + while (scounter[i] < 0) + { + sphase[i] = (sphase[i] + 1) & 15; + scounter[i] += freq[i] + 1; + } + + INT32 ret = 0; + if (length_counter[i] > 0 && + freq[i] >= 8 && + sfreq[i] < 0x800 + ) + { + int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; + ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; + } + + return ret; + } + + bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + if (0x4000 <= adr && adr < 0x4008) + { + val |= reg[adr&0x7]; + return true; + } + else if(adr==0x4015) + { + val |= (length_counter[1]?2:0)|(length_counter[0]?1:0); + return true; + } + else + return false; + } + + void NES_APU::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0, clocks); + out[1] = calc_sqr(1, clocks); + } + + // ¶¬‚³‚ê‚é”gŒ`‚ÌU•‚Í0-8191 + UINT32 NES_APU::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + + INT32 m[2]; + + if(option[OPT_NONLINEAR_MIXER]) + { + INT32 voltage = square_table[out[0] + out[1]]; + m[0] = out[0] << 6; + m[1] = out[1] << 6; + INT32 ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + } + else + { + m[0] = (out[0] * square_linear) / 15; + m[1] = (out[1] * square_linear) / 15; + } + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] >>= 7; + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] >>= 7; + + return 2; + } + + NES_APU::NES_APU () + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + option[OPT_UNMUTE_ON_RESET] = true; + option[OPT_PHASE_REFRESH] = true; + option[OPT_NONLINEAR_MIXER] = true; + option[OPT_DUTY_SWAP] = false; + option[OPT_NEGATE_SWEEP_INIT] = false; + + square_table[0] = 0; + for(int i=1;i<32;i++) + square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear + + for(int c=0;c<2;++c) + for(int t=0;t<2;++t) + sm[c][t] = 128; + } + + NES_APU::~NES_APU () + { + } + + void NES_APU::Reset () + { + int i; + gclock = 0; + mask = 0; + + for (int i=0; i<2; ++i) + { + scounter[i] = 0; + sphase[i] = 0; + duty[i] = 0; + volume[i] = 0; + freq[i] = 0; + sfreq[i] = 0; + sweep_enable[i] = 0; + sweep_mode[i] = 0; + sweep_write[i] = 0; + sweep_div_period[i] = 0; + sweep_div[i] = 1; + sweep_amount[i] = 0; + envelope_disable[i] = 0; + envelope_loop[i] = 0; + envelope_write[i] = 0; + envelope_div_period[i] = 0; + envelope_div[0] = 0; + envelope_counter[i] = 0; + length_counter[i] = 0; + enable[i] = 0; + } + + for (i = 0x4000; i < 0x4008; i++) + Write (i, 0); + + Write (0x4015, 0); + if (option[OPT_UNMUTE_ON_RESET]) + Write (0x4015, 0x0f); + if (option[OPT_NEGATE_SWEEP_INIT]) + { + Write (0x4001, 0x08); + Write (0x4005, 0x08); + } + + for (i = 0; i < 2; i++) + out[i] = 0; + + SetRate(rate); + } + + void NES_APU::SetOption (int id, int val) + { + if(id 1) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_APU::GetTrackInfo(int trk) + { + trkinfo[trk]._freq = freq[trk]; + if(freq[trk]) + trkinfo[trk].freq = clock/16/(freq[trk] + 1); + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = out[trk]; + trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); + trkinfo[trk].key = + enable[trk] && + length_counter[trk] > 0 && + freq[trk] >= 8 && + sfreq[trk] < 0x800 && + (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); + trkinfo[trk].tone = duty[trk]; + trkinfo[trk].max_volume = 15; + return &trkinfo[trk]; + } + + bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if (0x4000 <= adr && adr < 0x4008) + { + //DEBUG_OUT("$%04X = %02X\n",adr,val); + + adr &= 0xf; + ch = adr >> 2; + switch (adr) + { + case 0x0: + case 0x4: + volume[ch] = val & 15; + envelope_disable[ch] = (val >> 4) & 1; + envelope_loop[ch] = (val >> 5) & 1; + envelope_div_period[ch] = (val & 15); + duty[ch] = (val >> 6) & 3; + if (option[OPT_DUTY_SWAP]) + { + if (duty[ch] == 1) duty[ch] = 2; + else if (duty[ch] == 2) duty[ch] = 1; + } + break; + + case 0x1: + case 0x5: + sweep_enable[ch] = (val >> 7) & 1; + sweep_div_period[ch] = (((val >> 4) & 7)); + sweep_mode[ch] = (val >> 3) & 1; + sweep_amount[ch] = val & 7; + sweep_write[ch] = true; + sweep_sqr(ch); + break; + + case 0x2: + case 0x6: + freq[ch] = val | (freq[ch] & 0x700) ; + sweep_sqr(ch); + break; + + case 0x3: + case 0x7: + freq[ch] = (freq[ch] & 0xFF) | ((val & 0x7) << 8) ; + if (option[OPT_PHASE_REFRESH]) + sphase[ch] = 0; + envelope_write[ch] = true; + if (enable[ch]) + { + length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + sweep_sqr(ch); + break; + + default: + return false; + } + reg[adr] = val; + return true; + } + else if (adr == 0x4015) + { + enable[0] = (val & 1) ? true : false; + enable[1] = (val & 2) ? true : false; + + if (!enable[0]) + length_counter[0] = 0; + if (!enable[1]) + length_counter[1] = 0; + + reg[adr-0x4000] = val; + return true; + } + + // 4017 is handled in nes_dmc.cpp + //else if (adr == 0x4017) + //{ + //} + + return false; + } +} // namespace xgm; diff --git a/extern/NSFplay/nes_apu.h b/extern/NSFplay/nes_apu.h new file mode 100644 index 000000000..2a8e75412 --- /dev/null +++ b/extern/NSFplay/nes_apu.h @@ -0,0 +1,88 @@ +#ifndef _NES_APU_H_ +#define _NES_APU_H_ +#include "../device.h" +#include "nes_dmc.h" + +namespace xgm +{ + /** Upper half of APU **/ + class NES_APU : public ISoundChip + { + public: + enum + { + OPT_UNMUTE_ON_RESET=0, + OPT_PHASE_REFRESH, + OPT_NONLINEAR_MIXER, + OPT_DUTY_SWAP, + OPT_NEGATE_SWEEP_INIT, + OPT_END }; + + enum + { SQR0_MASK = 1, SQR1_MASK = 2, }; + + protected: + int option[OPT_END]; // ŠeŽíƒIƒvƒVƒ‡ƒ“ + int mask; + INT32 sm[2][2]; + + UINT32 gclock; + UINT8 reg[0x20]; + INT32 out[2]; + double rate, clock; + + INT32 square_table[32]; // nonlinear mixer + INT32 square_linear; // linear mix approximation + + int scounter[2]; // frequency divider + int sphase[2]; // phase counter + + int duty[2]; + int volume[2]; + int freq[2]; + int sfreq[2]; + + bool sweep_enable[2]; + bool sweep_mode[2]; + bool sweep_write[2]; + int sweep_div_period[2]; + int sweep_div[2]; + int sweep_amount[2]; + + bool envelope_disable[2]; + bool envelope_loop[2]; + bool envelope_write[2]; + int envelope_div_period[2]; + int envelope_div[2]; + int envelope_counter[2]; + + int length_counter[2]; + + bool enable[2]; + + void sweep_sqr (int ch); // calculates target sweep frequency + INT32 calc_sqr (int ch, UINT32 clocks); + TrackInfoBasic trkinfo[2]; + + public: + NES_APU (); + ~NES_APU (); + + void FrameSequence(int s); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 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, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_dmc.cpp b/extern/NSFplay/nes_dmc.cpp new file mode 100644 index 000000000..8073a22ca --- /dev/null +++ b/extern/NSFplay/nes_dmc.cpp @@ -0,0 +1,771 @@ +#include "nes_dmc.h" +#include "nes_apu.h" +#include + +namespace xgm +{ + const UINT32 NES_DMC::wavlen_table[2][16] = { + { // NTSC + 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 + }, + { // PAL + 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 + }}; + + const UINT32 NES_DMC::freq_table[2][16] = { + { // NTSC + 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 + }, + { // PAL + 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 + }}; + + const UINT32 BITREVERSE[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, + }; + + NES_DMC::NES_DMC () : GETA_BITS (20) + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + SetPal (false); + option[OPT_ENABLE_4011] = 1; + option[OPT_ENABLE_PNOISE] = 1; + option[OPT_UNMUTE_ON_RESET] = 1; + option[OPT_DPCM_ANTI_CLICK] = 0; + option[OPT_NONLINEAR_MIXER] = 1; + option[OPT_RANDOMIZE_NOISE] = 1; + option[OPT_RANDOMIZE_TRI] = 1; + option[OPT_TRI_MUTE] = 1; + option[OPT_DPCM_REVERSE] = 0; + tnd_table[0][0][0][0] = 0; + tnd_table[1][0][0][0] = 0; + + apu = NULL; + frame_sequence_count = 0; + frame_sequence_length = 7458; + frame_sequence_steps = 4; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + + NES_DMC::~NES_DMC () + { + } + + void NES_DMC::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_DMC::GetTrackInfo(int trk) + { + switch(trk) + { + case 0: + trkinfo[trk].max_volume = 255; + trkinfo[0].key = (linear_counter>0 && length_counter[0]>0 && enable[0]); + trkinfo[0].volume = 0; + trkinfo[0]._freq = tri_freq; + if(trkinfo[0]._freq) + trkinfo[0].freq = clock/32/(trkinfo[0]._freq + 1); + else + trkinfo[0].freq = 0; + trkinfo[0].tone = -1; + trkinfo[0].output = out[0]; + break; + case 1: + trkinfo[1].max_volume = 15; + trkinfo[1].volume = noise_volume+(envelope_disable?0:0x10)+(envelope_loop?0x20:0); + trkinfo[1].key = length_counter[1]>0 && enable[1] && + (envelope_disable ? (noise_volume>0) : (envelope_counter>0)); + trkinfo[1]._freq = reg[0x400e - 0x4008]&0xF; + trkinfo[1].freq = clock/double(wavlen_table[pal][trkinfo[1]._freq] * ((noise_tap&(1<<6)) ? 93 : 1)); + trkinfo[1].tone = noise_tap & (1<<6); + trkinfo[1].output = out[1]; + break; + case 2: + trkinfo[2].max_volume = 127; + trkinfo[2].volume = reg[0x4011 - 0x4008]&0x7F; + trkinfo[2].key = dlength > 0; + trkinfo[2]._freq = reg[0x4010 - 0x4008]&0xF; + trkinfo[2].freq = clock/double(freq_table[pal][trkinfo[2]._freq]); + trkinfo[2].tone = (0xc000|(adr_reg<<6)); + trkinfo[2].output = (damp<<1)|dac_lsb; + break; + default: + return NULL; + } + return &trkinfo[trk]; + } + + void NES_DMC::FrameSequence(int s) + { + //DEBUG_OUT("FrameSequence: %d\n",s); + + if (s > 3) return; // no operation in step 4 + + if (apu) + { + apu->FrameSequence(s); + } + + if (s == 0 && (frame_sequence_steps == 4)) + { + if (frame_irq_enable) frame_irq = true; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, frame_irq & frame_irq_enable); + } + + // 240hz clock + { + // triangle linear counter + if (linear_counter_halt) + { + linear_counter = linear_counter_reload; + } + else + { + if (linear_counter > 0) --linear_counter; + } + if (!linear_counter_control) + { + linear_counter_halt = false; + } + + // noise envelope + bool divider = false; + if (envelope_write) + { + envelope_write = false; + envelope_counter = 15; + envelope_div = 0; + } + else + { + ++envelope_div; + if (envelope_div > envelope_div_period) + { + divider = true; + envelope_div = 0; + } + } + if (divider) + { + if (envelope_loop && envelope_counter == 0) + envelope_counter = 15; + else if (envelope_counter > 0) + --envelope_counter; + } + } + + // 120hz clock + if ((s&1) == 0) + { + // triangle length counter + if (!linear_counter_control && (length_counter[0] > 0)) + --length_counter[0]; + + // noise length counter + if (!envelope_loop && (length_counter[1] > 0)) + --length_counter[1]; + } + + } + + // ŽOŠp”gƒ`ƒƒƒ“ƒlƒ‹‚ÌŒvŽZ –ß‚è’l‚Í0-15 + UINT32 NES_DMC::calc_tri (UINT32 clocks) + { + static UINT32 tritbl[32] = + { + 15,14,13,12,11,10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15, + }; + + if (linear_counter > 0 && length_counter[0] > 0 + && (!option[OPT_TRI_MUTE] || tri_freq > 0)) + { + counter[0] -= clocks; + while (counter[0] < 0) + { + tphase = (tphase + 1) & 31; + counter[0] += (tri_freq + 1); + } + } + + UINT32 ret = tritbl[tphase]; + return ret; + } + + // ƒmƒCƒYƒ`ƒƒƒ“ƒlƒ‹‚ÌŒvŽZ –ß‚è’l‚Í0-127 + // ’áƒTƒ“ƒvƒŠƒ“ƒOƒŒ[ƒg‚Ҭ‚·‚邯ƒGƒCƒŠƒAƒXƒmƒCƒY‚ªŒƒ‚µ‚¢‚̂Š+ // ƒmƒCƒY‚¾‚¯‚Í‚±‚ÌŠÖ”“à‚Å‚ƒNƒƒbƒN‡¬‚µAŠÈˆÕ‚ȃTƒ“ƒvƒŠƒ“ƒOƒŒ[ƒg + // •ÏŠ·‚ðs‚Á‚Ä‚¢‚éB + UINT32 NES_DMC::calc_noise(UINT32 clocks) + { + UINT32 env = envelope_disable ? noise_volume : envelope_counter; + if (length_counter[1] < 1) env = 0; + + UINT32 last = (noise & 0x4000) ? 0 : env; + if (clocks < 1) return last; + + // simple anti-aliasing (noise requires it, even when oversampling is off) + UINT32 count = 0; + UINT32 accum = counter[1] * last; // samples pending from previous calc + UINT32 accum_clocks = counter[1]; + #ifdef _DEBUG + INT32 start_clocks = counter[1]; + #endif + if (counter[1] < 0) // only happens on startup when using the randomize noise option + { + accum = 0; + accum_clocks = 0; + } + + counter[1] -= clocks; + assert (nfreq > 0); // prevent infinite loop + while (counter[1] < 0) + { + // tick the noise generator + UINT32 feedback = (noise&1) ^ ((noise&noise_tap)?1:0); + noise = (noise>>1) | (feedback<<14); + + last = (noise & 0x4000) ? 0 : env; + accum += (last * nfreq); + counter[1] += nfreq; + ++count; + accum_clocks += nfreq; + } + + if (count < 1) // no change over interval, don't anti-alias + { + return last; + } + + accum -= (last * counter[1]); // remove these samples which belong in the next calc + accum_clocks -= counter[1]; + #ifdef _DEBUG + if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal + #endif + + UINT32 average = accum / accum_clocks; + assert(average <= 15); // above this would indicate overflow + return average; + } + + // Tick the DMC for the number of clocks, and return output counter; + UINT32 NES_DMC::calc_dmc (UINT32 clocks) + { + counter[2] -= clocks; + assert (dfreq > 0); // prevent infinite loop + while (counter[2] < 0) + { + counter[2] += dfreq; + + if ( data > 0x100 ) // data = 0x100 when shift register is empty + { + if (!empty) + { + if ((data & 1) && (damp < 63)) + damp++; + else if (!(data & 1) && (0 < damp)) + damp--; + } + data >>=1; + } + + if ( data <= 0x100 ) // shift register is empty + { + if (dlength > 0) + { + memory->Read (daddress, data); + cpu->StealCycles(4); // DMC read takes 3 or 4 CPU cycles, usually 4 + // (checking for the 3-cycle case would require sub-instruction emulation) + data &= 0xFF; // read 8 bits + if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data]; + data |= 0x10000; // use an extra bit to signal end of data + empty = false; + daddress = ((daddress+1)&0xFFFF)|0x8000 ; + --dlength; + if (dlength == 0) + { + if (mode & 1) // looped DPCM = auto-reload + { + daddress = ((adr_reg<<6)|0xC000); + dlength = (len_reg<<4)+1; + } + else if (mode & 2) // IRQ and not looped + { + irq = true; + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, true); + } + } + } + else + { + data = 0x10000; // DMC will do nothing + empty = true; + } + } + } + + return (damp<<1) + dac_lsb; + } + + void NES_DMC::TickFrameSequence (UINT32 clocks) + { + frame_sequence_count += clocks; + while (frame_sequence_count > frame_sequence_length) + { + FrameSequence(frame_sequence_step); + frame_sequence_count -= frame_sequence_length; + ++frame_sequence_step; + if(frame_sequence_step >= frame_sequence_steps) + frame_sequence_step = 0; + } + } + + void NES_DMC::Tick (UINT32 clocks) + { + out[0] = calc_tri(clocks); + out[1] = calc_noise(clocks); + out[2] = calc_dmc(clocks); + } + + UINT32 NES_DMC::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + out[2] = (mask & 4) ? 0 : out[2]; + + INT32 m[3]; + m[0] = tnd_table[0][out[0]][0][0]; + m[1] = tnd_table[0][0][out[1]][0]; + m[2] = tnd_table[0][0][0][out[2]]; + + if (option[OPT_NONLINEAR_MIXER]) + { + INT32 ref = m[0] + m[1] + m[2]; + INT32 voltage = tnd_table[1][out[0]][out[1]][out[2]]; + if (ref) + { + for (int i=0; i < 3; ++i) + m[i] = (m[i] * voltage) / ref; + } + else + { + for (int i=0; i < 3; ++i) + m[i] = voltage; + } + } + + // anti-click nullifies any 4011 write but preserves nonlinearity + if (option[OPT_DPCM_ANTI_CLICK]) + { + if (dmc_pop) // $4011 will cause pop this frame + { + // adjust offset to counteract pop + dmc_pop_offset += dmc_pop_follow - m[2]; + dmc_pop = false; + + // prevent overflow, keep headspace at edges + const INT32 OFFSET_MAX = (1 << 30) - (4 << 16); + if (dmc_pop_offset > OFFSET_MAX) dmc_pop_offset = OFFSET_MAX; + if (dmc_pop_offset < -OFFSET_MAX) dmc_pop_offset = -OFFSET_MAX; + } + dmc_pop_follow = m[2]; // remember previous position + + m[2] += dmc_pop_offset; // apply offset + + // TODO implement this in a better way + // roll off offset (not ideal, but prevents overflow) + if (dmc_pop_offset > 0) --dmc_pop_offset; + else if (dmc_pop_offset < 0) ++dmc_pop_offset; + } + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] += m[2] * sm[0][2]; + b[0] >>= 7; + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] += m[2] * sm[1][2]; + b[1] >>= 7; + + return 2; + } + + void NES_DMC::SetClock (double c) + { + clock = c; + } + + void NES_DMC::SetRate (double r) + { + rate = (UINT32)(r?r:DEFAULT_RATE); + } + + void NES_DMC::SetPal (bool is_pal) + { + pal = (is_pal ? 1 : 0); + // set CPU cycles in frame_sequence + frame_sequence_length = is_pal ? 8314 : 7458; + } + + void NES_DMC::SetAPU (NES_APU* apu_) + { + apu = apu_; + } + + // Initializing TRI, NOISE, DPCM mixing table + void NES_DMC::InitializeTNDTable(double wt, double wn, double wd) { + + // volume adjusted by 0.95 based on empirical measurements + const double MASTER = 8192.0 * 0.95; + // truthfully, the nonlinear curve does not appear to match well + // with my tests. Do more testing of the APU/DMC DAC later. + // this value keeps the triangle consistent with measured levels, + // but not necessarily the rest of this APU channel, + // because of the lack of a good DAC model, currently. + + { // Linear Mixer + for(int t=0; t<16 ; t++) { + for(int n=0; n<16; n++) { + for(int d=0; d<128; d++) { + tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0); + } + } + } + } + { // Non-Linear Mixer + tnd_table[1][0][0][0] = 0; + for(int t=0; t<16 ; t++) { + for(int n=0; n<16; n++) { + for(int d=0; d<128; d++) { + if(t!=0||n!=0||d!=0) + tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); + } + } + } + } + + } + + void NES_DMC::Reset () + { + int i; + mask = 0; + + InitializeTNDTable(8227,12241,22638); + + counter[0] = 0; + counter[1] = 0; + counter[2] = 0; + tphase = 0; + nfreq = wavlen_table[0][0]; + dfreq = freq_table[0][0]; + tri_freq = 0; + linear_counter = 0; + linear_counter_reload = 0; + linear_counter_halt = 0; + linear_counter_control = 0; + noise_volume = 0; + noise = 0; + noise_tap = 0; + envelope_loop = 0; + envelope_disable = 0; + envelope_write = 0; + envelope_div_period = 0; + envelope_div = 0; + envelope_counter = 0; + enable[0] = 0; + enable[1] = 0; + length_counter[0] = 0; + length_counter[1] = 0; + frame_irq = false; + frame_irq_enable = false; + frame_sequence_count = 0; + frame_sequence_steps = 4; + frame_sequence_step = 0; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + + for (i = 0; i < 0x0F; i++) + Write (0x4008 + i, 0); + Write (0x4017, 0x40); + + irq = false; + Write (0x4015, 0x00); + if (option[OPT_UNMUTE_ON_RESET]) + Write (0x4015, 0x0f); + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); + + out[0] = out[1] = out[2] = 0; + damp = 0; + dmc_pop = false; + dmc_pop_offset = 0; + dmc_pop_follow = 0; + dac_lsb = 0; + data = 0x100; + empty = true; + adr_reg = 0; + dlength = 0; + len_reg = 0; + daddress = 0; + noise = 1; + noise_tap = (1<<1); + + if (option[OPT_RANDOMIZE_NOISE]) + { + noise |= ::rand(); + counter[1] = -(rand() & 511); + } + if (option[OPT_RANDOMIZE_TRI]) + { + tphase = ::rand() & 31; + counter[0] = -(rand() & 2047); + } + + SetRate(rate); + } + + void NES_DMC::SetMemory (IDevice * r) + { + memory = r; + } + + void NES_DMC::SetOption (int id, int val) + { + if(idUpdateIRQ(NES_CPU::IRQD_DMC, false); + + reg[adr-0x4008] = val; + return true; + } + + if (adr == 0x4017) + { + //DEBUG_OUT("4017 = %02X\n", val); + frame_irq_enable = ((val & 0x40) != 0x40); + if (frame_irq_enable) frame_irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + + frame_sequence_count = 0; + if (val & 0x80) + { + frame_sequence_steps = 5; + frame_sequence_step = 0; + FrameSequence(frame_sequence_step); + ++frame_sequence_step; + } + else + { + frame_sequence_steps = 4; + frame_sequence_step = 1; + } + } + + if (adr<0x4008||0x4013> 7) & 1; + linear_counter_reload = val & 0x7F; + break; + + case 0x4009: + break; + + case 0x400a: + tri_freq = val | (tri_freq & 0x700) ; + break; + + case 0x400b: + tri_freq = (tri_freq & 0xff) | ((val & 0x7) << 8) ; + linear_counter_halt = true; + if (enable[0]) + { + length_counter[0] = length_table[(val >> 3) & 0x1f]; + } + break; + + // noise + + case 0x400c: + noise_volume = val & 15; + envelope_div_period = val & 15; + envelope_disable = (val >> 4) & 1; + envelope_loop = (val >> 5) & 1; + break; + + case 0x400d: + break; + + case 0x400e: + if (option[OPT_ENABLE_PNOISE]) + noise_tap = (val & 0x80) ? (1<<6) : (1<<1); + else + noise_tap = (1<<1); + nfreq = wavlen_table[pal][val&15]; + break; + + case 0x400f: + if (enable[1]) + { + length_counter[1] = length_table[(val >> 3) & 0x1f]; + } + envelope_write = true; + break; + + // dmc + + case 0x4010: + mode = (val >> 6) & 3; + if (!(mode & 2)) + { + irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); + } + dfreq = freq_table[pal][val&15]; + break; + + case 0x4011: + if (option[OPT_ENABLE_4011]) + { + damp = (val >> 1) & 0x3f; + dac_lsb = val & 1; + dmc_pop = true; + } + break; + + case 0x4012: + adr_reg = val&0xff; + // ‚±‚±‚Ådaddress‚ÍXV‚³‚ê‚È‚¢ + break; + + case 0x4013: + len_reg = val&0xff; + // ‚±‚±‚Ålength‚ÍXV‚³‚ê‚È‚¢ + break; + + default: + return false; + } + + return true; + } + + bool NES_DMC::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + if (adr == 0x4015) + { + val |=(irq ? 0x80 : 0) + | (frame_irq ? 0x40 : 0) + | ((dlength>0) ? 0x10 : 0) + | (length_counter[1] ? 0x08 : 0) + | (length_counter[0] ? 0x04 : 0) + ; + + frame_irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + return true; + } + else if (0x4008<=adr&&adr<=0x4014) + { + val |= reg[adr-0x4008]; + return true; + } + else + return false; + } + + // IRQ support requires CPU read access + void NES_DMC::SetCPU(NES_CPU* cpu_) + { + cpu = cpu_; + } +} // namespace diff --git a/extern/NSFplay/nes_dmc.h b/extern/NSFplay/nes_dmc.h new file mode 100644 index 000000000..9152584aa --- /dev/null +++ b/extern/NSFplay/nes_dmc.h @@ -0,0 +1,129 @@ +#ifndef _NES_DMC_H_ +#define _NES_DMC_H_ + +#include "../device.h" +#include "../Audio/MedianFilter.h" +#include "../CPU/nes_cpu.h" + +namespace xgm +{ + class NES_APU; // forward declaration + + /** Bottom Half of APU **/ + class NES_DMC:public ISoundChip + { + public: + enum + { + OPT_ENABLE_4011=0, + OPT_ENABLE_PNOISE, + OPT_UNMUTE_ON_RESET, + OPT_DPCM_ANTI_CLICK, + OPT_NONLINEAR_MIXER, + OPT_RANDOMIZE_NOISE, + OPT_TRI_MUTE, + OPT_RANDOMIZE_TRI, + OPT_DPCM_REVERSE, + OPT_END + }; + protected: + const int GETA_BITS; + static const UINT32 freq_table[2][16]; + static const UINT32 wavlen_table[2][16]; + UINT32 tnd_table[2][16][16][128]; + + int option[OPT_END]; + int mask; + INT32 sm[2][3]; + UINT8 reg[0x10]; + UINT32 len_reg; + UINT32 adr_reg; + IDevice *memory; + UINT32 out[3]; + UINT32 daddress; + UINT32 dlength; + UINT32 data; + bool empty; + INT16 damp; + int dac_lsb; + bool dmc_pop; + INT32 dmc_pop_offset; + INT32 dmc_pop_follow; + double clock; + UINT32 rate; + int pal; + int mode; + bool irq; + + INT32 counter[3]; // frequency dividers + int tphase; // triangle phase + UINT32 nfreq; // noise frequency + UINT32 dfreq; // DPCM frequency + + UINT32 tri_freq; + int linear_counter; + int linear_counter_reload; + bool linear_counter_halt; + bool linear_counter_control; + + int noise_volume; + UINT32 noise, noise_tap; + + // noise envelope + bool envelope_loop; + bool envelope_disable; + bool envelope_write; + int envelope_div_period; + int envelope_div; + int envelope_counter; + + bool enable[2]; // tri/noise enable + int length_counter[2]; // 0=tri, 1=noise + + TrackInfoBasic trkinfo[3]; + + // frame sequencer + NES_APU* apu; // apu is clocked by DMC's frame sequencer + int frame_sequence_count; // current cycle count + int frame_sequence_length; // CPU cycles per FrameSequence + int frame_sequence_step; // current step of frame sequence + int frame_sequence_steps; // 4/5 steps per frame + bool frame_irq; + bool frame_irq_enable; + + NES_CPU* cpu; // IRQ needs CPU access + + inline UINT32 calc_tri (UINT32 clocks); + inline UINT32 calc_dmc (UINT32 clocks); + inline UINT32 calc_noise (UINT32 clocks); + + public: + NES_DMC (); + ~NES_DMC (); + + void InitializeTNDTable(double wt, double wn, double wd); + void SetPal (bool is_pal); + void SetAPU (NES_APU* apu_); + void SetMemory (IDevice * r); + void FrameSequence(int s); + int GetDamp(){ return (damp<<1)|dac_lsb ; } + void TickFrameSequence (UINT32 clocks); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 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, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + + void SetCPU(NES_CPU* cpu_); + }; + +} + +#endif diff --git a/extern/NSFplay/nes_fds.cpp b/extern/NSFplay/nes_fds.cpp new file mode 100644 index 000000000..9d74c62f0 --- /dev/null +++ b/extern/NSFplay/nes_fds.cpp @@ -0,0 +1,397 @@ +#include +#include "nes_fds.h" + +namespace xgm { + +const int RC_BITS = 12; + +NES_FDS::NES_FDS () +{ + option[OPT_CUTOFF] = 2000; + option[OPT_4085_RESET] = 0; + option[OPT_WRITE_PROTECT] = 0; // not used here, see nsfplay.cpp + + rc_k = 0; + rc_l = (1< 1) return; + sm[0] = mixl; + sm[1] = mixr; +} + +ITrackInfo *NES_FDS::GetTrackInfo(int trk) +{ + trkinfo.max_volume = 32; + trkinfo.volume = last_vol; + trkinfo.key = last_vol > 0; + trkinfo._freq = last_freq; + trkinfo.freq = (double(last_freq) * clock) / (65536.0 * 64.0); + trkinfo.tone = env_out[EMOD]; + for(int i=0;i<64;i++) + trkinfo.wave[i] = wave[TWAV][i]; + + return &trkinfo; +} + +void NES_FDS::SetClock (double c) +{ + clock = c; +} + +void NES_FDS::SetRate (double r) +{ + rate = r; + + // configure lowpass filter + double cutoff = double(option[OPT_CUTOFF]); + double leak = 0.0; + if (cutoff > 0) + leak = ::exp(-2.0 * 3.14159 * cutoff / rate); + rc_k = INT32(leak * double(1<= period) + { + // clock the envelope + if (env_mode[i]) + { + if (env_out[i] < 32) ++env_out[i]; + } + else + { + if (env_out[i] > 0 ) --env_out[i]; + } + env_timer[i] -= period; + } + } + } + } + + // clock the mod table + if (!mod_halt) + { + // advance phase, adjust for modulator + UINT32 start_pos = phase[TMOD] >> 16; + phase[TMOD] += (clocks * freq[TMOD]); + UINT32 end_pos = phase[TMOD] >> 16; + + // wrap the phase to the 64-step table (+ 16 bit accumulator) + phase[TMOD] = phase[TMOD] & 0x3FFFFF; + + // execute all clocked steps + for (UINT32 p = start_pos; p < end_pos; ++p) + { + INT32 wv = wave[TMOD][p & 0x3F]; + if (wv == 4) // 4 resets mod position + mod_pos = 0; + else + { + const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; + mod_pos += BIAS[wv]; + mod_pos &= 0x7F; // 7-bit clamp + } + } + } + + // clock the wav table + if (!wav_halt) + { + // complex mod calculation + INT32 mod = 0; + if (env_out[EMOD] != 0) // skip if modulator off + { + // convert mod_pos to 7-bit signed + INT32 pos = (mod_pos < 64) ? mod_pos : (mod_pos-128); + + // multiply pos by gain, + // shift off 4 bits but with odd "rounding" behaviour + INT32 temp = pos * env_out[EMOD]; + INT32 rem = temp & 0x0F; + temp >>= 4; + if ((rem > 0) && ((temp & 0x80) == 0)) + { + if (pos < 0) temp -= 1; + else temp += 2; + } + + // wrap if range is exceeded + while (temp >= 192) temp -= 256; + while (temp < -64) temp += 256; + + // multiply result by pitch, + // shift off 6 bits, round to nearest + temp = freq[TWAV] * temp; + rem = temp & 0x3F; + temp >>= 6; + if (rem >= 32) temp += 1; + + mod = temp; + } + + // advance wavetable position + INT32 f = freq[TWAV] + mod; + phase[TWAV] = phase[TWAV] + (clocks * f); + phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap + + // store for trackinfo + last_freq = f; + } + + // output volume caps at 32 + INT32 vol_out = env_out[EVOL]; + if (vol_out > 32) vol_out = 32; + + // final output + if (!wav_write) + fout = wave[TWAV][(phase[TWAV]>>16)&0x3F] * vol_out; + + // NOTE: during wav_halt, the unit still outputs (at phase 0) + // and volume can affect it if the first sample is nonzero. + // haven't worked out 100% of the conditions for volume to + // effect (vol envelope does not seem to run, but am unsure) + // but this implementation is very close to correct + + // store for trackinfo + last_vol = vol_out; +} + +UINT32 NES_FDS::Render (INT32 b[2]) +{ + // 8 bit approximation of master volume + const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223) + const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol + const INT32 MASTER[4] = { + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) }; + + INT32 v = fout * MASTER[master_vol] >> 8; + + // lowpass RC filter + INT32 rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS; + rc_accum = rc_out; + v = rc_out; + + // output mix + INT32 m = mask ? 0 : v; + b[0] = (m * sm[0]) >> 7; + b[1] = (m * sm[1]) >> 7; + return 2; +} + +bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id) +{ + // $4023 master I/O enable/disable + if (adr == 0x4023) + { + master_io = ((val & 2) != 0); + return true; + } + + if (!master_io) + return false; + if (adr < 0x4040 || adr > 0x408A) + return false; + + if (adr < 0x4080) // $4040-407F wave table write + { + if (wav_write) + wave[TWAV][adr - 0x4040] = val & 0x3F; + return true; + } + + switch (adr & 0x00FF) + { + case 0x80: // $4080 volume envelope + env_disable[EVOL] = ((val & 0x80) != 0); + env_mode[EVOL] = ((val & 0x40) != 0); + env_timer[EVOL] = 0; + env_speed[EVOL] = val & 0x3F; + if (env_disable[EVOL]) + env_out[EVOL] = env_speed[EVOL]; + return true; + case 0x81: // $4081 --- + return false; + case 0x82: // $4082 wave frequency low + freq[TWAV] = (freq[TWAV] & 0xF00) | val; + return true; + case 0x83: // $4083 wave frequency high / enables + freq[TWAV] = (freq[TWAV] & 0x0FF) | ((val & 0x0F) << 8); + wav_halt = ((val & 0x80) != 0); + env_halt = ((val & 0x40) != 0); + if (wav_halt) + phase[TWAV] = 0; + if (env_halt) + { + env_timer[EMOD] = 0; + env_timer[EVOL] = 0; + } + return true; + case 0x84: // $4084 mod envelope + env_disable[EMOD] = ((val & 0x80) != 0); + env_mode[EMOD] = ((val & 0x40) != 0); + env_timer[EMOD] = 0; + env_speed[EMOD] = val & 0x3F; + if (env_disable[EMOD]) + env_out[EMOD] = env_speed[EMOD]; + return true; + case 0x85: // $4085 mod position + mod_pos = val & 0x7F; + // not hardware accurate., but prevents detune due to cycle inaccuracies + // (notably in Bio Miracle Bokutte Upa) + if (option[OPT_4085_RESET]) + phase[TMOD] = mod_write_pos << 16; + return true; + case 0x86: // $4086 mod frequency low + freq[TMOD] = (freq[TMOD] & 0xF00) | val; + return true; + case 0x87: // $4087 mod frequency high / enable + freq[TMOD] = (freq[TMOD] & 0x0FF) | ((val & 0x0F) << 8); + mod_halt = ((val & 0x80) != 0); + if (mod_halt) + phase[TMOD] = phase[TMOD] & 0x3F0000; // reset accumulator phase + return true; + case 0x88: // $4088 mod table write + if (mod_halt) + { + // writes to current playback position (there is no direct way to set phase) + wave[TMOD][(phase[TMOD] >> 16) & 0x3F] = val & 0x07; + phase[TMOD] = (phase[TMOD] + 0x010000) & 0x3FFFFF; + wave[TMOD][(phase[TMOD] >> 16) & 0x3F] = val & 0x07; + phase[TMOD] = (phase[TMOD] + 0x010000) & 0x3FFFFF; + mod_write_pos = phase[TMOD] >> 16; // used by OPT_4085_RESET + } + return true; + case 0x89: // $4089 wave write enable, master volume + wav_write = ((val & 0x80) != 0); + master_vol = val & 0x03; + return true; + case 0x8A: // $408A envelope speed + master_env_speed = val; + // haven't tested whether this register resets phase on hardware, + // but this ensures my inplementation won't spam envelope clocks + // if this value suddenly goes low. + env_timer[EMOD] = 0; + env_timer[EVOL] = 0; + return true; + default: + return false; + } + return false; +} + +bool NES_FDS::Read (UINT32 adr, UINT32 & val, UINT32 id) +{ + if (adr >= 0x4040 && adr <= 0x407F) + { + // TODO: if wav_write is not enabled, the + // read address may not be reliable? need + // to test this on hardware. + val = wave[TWAV][adr - 0x4040]; + return true; + } + + if (adr == 0x4090) // $4090 read volume envelope + { + val = env_out[EVOL] | 0x40; + return true; + } + + if (adr == 0x4092) // $4092 read mod envelope + { + val = env_out[EMOD] | 0x40; + return true; + } + + return false; +} + +} // namespace diff --git a/extern/NSFplay/nes_fds.h b/extern/NSFplay/nes_fds.h new file mode 100644 index 000000000..212e472b4 --- /dev/null +++ b/extern/NSFplay/nes_fds.h @@ -0,0 +1,83 @@ +#ifndef _NES_FDS_H_ +#define _NES_FDS_H_ +#include "../device.h" + +namespace xgm { + +class TrackInfoFDS : public TrackInfoBasic +{ +public: + INT16 wave[64]; + virtual IDeviceInfo *Clone(){ return new TrackInfoFDS(*this); } +}; + +class NES_FDS : public ISoundChip +{ +public: + enum + { + OPT_CUTOFF=0, + OPT_4085_RESET, + OPT_WRITE_PROTECT, + OPT_END + }; + +protected: + double rate, clock; + int mask; + INT32 sm[2]; // stereo mix + INT32 fout; // current output + TrackInfoFDS trkinfo; + int option[OPT_END]; + + bool master_io; + UINT32 master_vol; + UINT32 last_freq; // for trackinfo + UINT32 last_vol; // for trackinfo + + // two wavetables + enum { TMOD=0, TWAV=1 }; + INT32 wave[2][64]; + UINT32 freq[2]; + UINT32 phase[2]; + bool wav_write; + bool wav_halt; + bool env_halt; + bool mod_halt; + UINT32 mod_pos; + UINT32 mod_write_pos; + + // two ramp envelopes + enum { EMOD=0, EVOL=1 }; + bool env_mode[2]; + bool env_disable[2]; + UINT32 env_timer[2]; + UINT32 env_speed[2]; + UINT32 env_out[2]; + UINT32 master_env_speed; + + // 1-pole RC lowpass filter + INT32 rc_accum; + INT32 rc_k; + INT32 rc_l; + +public: + NES_FDS (); + virtual ~ NES_FDS (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 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, INT16 mixl, INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); +}; + +} // namespace xgm + +#endif diff --git a/extern/NSFplay/nes_fme7.cpp b/extern/NSFplay/nes_fme7.cpp new file mode 100644 index 000000000..9d48de794 --- /dev/null +++ b/extern/NSFplay/nes_fme7.cpp @@ -0,0 +1,186 @@ +#include "nes_fme7.h" + +using namespace xgm; + +const int DIVIDER = 8; // TODO this is not optimal, rewrite PSG output + +NES_FME7::NES_FME7 () +{ + psg = PSG_new ((e_uint32)DEFAULT_CLOCK, DEFAULT_RATE); + divider = 0; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; +} + +NES_FME7::~NES_FME7 () +{ + if (psg) + PSG_delete (psg); +} + +void NES_FME7::SetClock (double c) +{ + this->clock = c * 2.0; +} + +void NES_FME7::SetRate (double r) +{ + //rate = r ? r : DEFAULT_RATE; + rate = DEFAULT_CLOCK / double(DIVIDER); // TODO rewrite PSG to integrate with clock + if (psg) + PSG_set_rate (psg, (e_uint32)rate); +} + +void NES_FME7::SetOption (int id, int val) +{ + if(id= DIVIDER) + { + divider -= DIVIDER; + if (psg) PSG_calc(psg); + } +} + +xgm::UINT32 NES_FME7::Render (xgm::INT32 b[2]) +{ + b[0] = b[1] = 0; + + for (int i=0; i < 3; ++i) + { + // note negative polarity + b[0] -= psg->cout[i] * sm[0][i]; + b[1] -= psg->cout[i] * sm[1][i]; + } + b[0] >>= (7-4); + b[1] >>= (7-4); + + // master volume adjustment + const INT32 MASTER = INT32(0.64 * 256.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; +} + +void NES_FME7::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) +{ + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; +} + +ITrackInfo *NES_FME7::GetTrackInfo(int trk) +{ + assert(trk<5); + + if(psg) + { + if (trk<3) + { + trkinfo[trk]._freq = psg->freq[trk]; + if(psg->freq[trk]) + trkinfo[trk].freq = psg->clk/32.0/psg->freq[trk]; + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = psg->cout[trk]; + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = psg->volume[trk] >> 1; + //trkinfo[trk].key = (psg->cout[trk]>0)?true:false; + trkinfo[trk].key = !(psg->tmask[trk]); + trkinfo[trk].tone = (psg->tmask[trk]?2:0)+(psg->nmask[trk]?1:0); + } + else if (trk == 3) // envelope + { + trkinfo[trk]._freq = psg->env_freq; + if(psg->env_freq) + trkinfo[trk].freq = psg->clk/512.0/psg->env_freq; + else + trkinfo[trk].freq = 0; + + if (psg->env_continue && psg->env_alternate && !psg->env_hold) // triangle wave + { + trkinfo[trk].freq *= 0.5f; // sounds an octave down + } + + trkinfo[trk].output = psg->voltbl[psg->env_ptr]; + trkinfo[trk].max_volume = 0; + trkinfo[trk].volume = 0; + trkinfo[trk].key = (((psg->volume[0]|psg->volume[1]|psg->volume[2])&32) != 0); + trkinfo[trk].tone = + (psg->env_continue ?8:0) | + (psg->env_attack ?4:0) | + (psg->env_alternate?2:0) | + (psg->env_hold ?1:0) ; + } + else if (trk == 4) // noise + { + trkinfo[trk]._freq = psg->noise_freq >> 1; + if(trkinfo[trk]._freq > 0) + trkinfo[trk].freq = psg->clk/16.0/psg->noise_freq; + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = psg->noise_seed & 1; + trkinfo[trk].max_volume = 0; + trkinfo[trk].volume = 0; + //trkinfo[trk].key = ((psg->nmask[0]&psg->nmask[1]&psg->nmask[2]) == 0); + trkinfo[trk].key = false; + trkinfo[trk].tone = 0; + } + } + return &trkinfo[trk]; +} diff --git a/extern/NSFplay/nes_fme7.h b/extern/NSFplay/nes_fme7.h new file mode 100644 index 000000000..d351b0859 --- /dev/null +++ b/extern/NSFplay/nes_fme7.h @@ -0,0 +1,42 @@ +#ifndef _NES_FME7_H_ +#define _NES_FME7_H_ +#include "../device.h" +#include "legacy/emu2149.h" + +namespace xgm +{ + + class NES_FME7:public ISoundChip + { + public: + enum + { + OPT_END + }; + protected: + //int option[OPT_END]; + INT32 sm[2][3]; // stereo mix + INT16 buf[2]; + PSG *psg; + int divider; // clock divider + double clock, rate; + TrackInfoBasic trkinfo[5]; + public: + NES_FME7 (); + ~NES_FME7 (); + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetOption (int, int); + virtual void SetMask (int m){ if(psg) PSG_setMask(psg,m); } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_mmc5.cpp b/extern/NSFplay/nes_mmc5.cpp new file mode 100644 index 000000000..2b2f8fcb7 --- /dev/null +++ b/extern/NSFplay/nes_mmc5.cpp @@ -0,0 +1,422 @@ +#include "nes_mmc5.h" + +namespace xgm +{ + + NES_MMC5::NES_MMC5 () + { + cpu = NULL; + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + option[OPT_NONLINEAR_MIXER] = true; + option[OPT_PHASE_REFRESH] = true; + frame_sequence_count = 0; + + // square nonlinear mix, same as 2A03 + square_table[0] = 0; + for(int i=1;i<32;i++) + square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + // 2A03 style nonlinear pcm mix with double the bits + //pcm_table[0] = 0; + //INT32 wd = 22638; + //for(int d=1;d<256; ++d) + // pcm_table[d] = (INT32)((8192.0*159.79)/(100.0+1.0/((double)d/wd))); + + // linear pcm mix (actual hardware seems closer to this) + pcm_table[0] = 0; + double pcm_scale = 32.0; + for (int d=1; d<256; ++d) + pcm_table[d] = (INT32)(double(d) * pcm_scale); + + // stereo mix + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + NES_MMC5::~NES_MMC5 () + { + } + + void NES_MMC5::Reset () + { + int i; + + scounter[0] = 0; + scounter[1] = 0; + sphase[0] = 0; + sphase[1] = 0; + + envelope_div[0] = 0; + envelope_div[1] = 0; + length_counter[0] = 0; + length_counter[1] = 0; + envelope_counter[0] = 0; + envelope_counter[1] = 0; + frame_sequence_count = 0; + + for (i = 0; i < 8; i++) + Write (0x5000 + i, 0); + + Write(0x5015, 0); + + for (i = 0; i < 3; ++i) + out[i] = 0; + + mask = 0; + pcm = 0; // PCM channel + pcm_mode = false; // write mode + + SetRate(rate); + } + + void NES_MMC5::SetOption (int id, int val) + { + if(idclock = c; + } + + void NES_MMC5::SetRate (double r) + { + rate = r ? r : DEFAULT_RATE; + } + + void NES_MMC5::FrameSequence () + { + // 240hz clock + for (int i=0; i < 2; ++i) + { + bool divider = false; + if (envelope_write[i]) + { + envelope_write[i] = false; + envelope_counter[i] = 15; + envelope_div[i] = 0; + } + else + { + ++envelope_div[i]; + if (envelope_div[i] > envelope_div_period[i]) + { + divider = true; + envelope_div[i] = 0; + } + } + if (divider) + { + if (envelope_loop[i] && envelope_counter[i] == 0) + envelope_counter[i] = 15; + else if (envelope_counter[i] > 0) + --envelope_counter[i]; + } + } + + // MMC5 length counter is clocked at 240hz, unlike 2A03 + for (int i=0; i < 2; ++i) + { + if (!envelope_loop[i] && (length_counter[i] > 0)) + --length_counter[i]; + } + } + + INT32 NES_MMC5::calc_sqr (int i, UINT32 clocks) + { + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + scounter[i] += clocks; + while (scounter[i] > freq[i]) + { + sphase[i] = (sphase[i] + 1) & 15; + scounter[i] -= (freq[i] + 1); + } + + INT32 ret = 0; + if (length_counter[i] > 0) + { + // note MMC5 does not silence the highest 8 frequencies like APU, + // because this is done by the sweep unit. + + int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; + ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; + } + + return ret; + } + + void NES_MMC5::TickFrameSequence (UINT32 clocks) + { + frame_sequence_count += clocks; + while (frame_sequence_count > 7458) + { + FrameSequence(); + frame_sequence_count -= 7458; + } + } + + void NES_MMC5::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0, clocks); + out[1] = calc_sqr(1, clocks); + out[2] = pcm; + } + + UINT32 NES_MMC5::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + out[2] = (mask & 4) ? 0 : out[2]; + + INT32 m[3]; + + if(option[OPT_NONLINEAR_MIXER]) + { + // squares nonlinear + INT32 voltage = square_table[out[0] + out[1]]; + m[0] = out[0] << 6; + m[1] = out[1] << 6; + INT32 ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + + // pcm nonlinear + m[2] = pcm_table[out[2]]; + } + else + { + // squares + m[0] = out[0] << 6; + m[1] = out[1] << 6; + + // pcm channel + m[2] = out[2] << 5; + } + + // note polarity is flipped on output + + b[0] = m[0] * -sm[0][0]; + b[0] += m[1] * -sm[0][1]; + b[0] += m[2] * -sm[0][2]; + b[0] >>= 7; + + b[1] = m[0] * -sm[1][0]; + b[1] += m[1] * -sm[1][1]; + b[1] += m[2] * -sm[1][2]; + b[1] >>= 7; + + return 2; + } + + bool NES_MMC5::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if ((0x5c00 <= adr) && (adr < 0x5ff0)) + { + ram[adr & 0x3ff] = val; + return true; + } + else if ((0x5000 <= adr) && (adr < 0x5008)) + { + reg[adr & 0x7] = val; + } + + switch (adr) + { + case 0x5000: + case 0x5004: + ch = (adr >> 2) & 1; + volume[ch] = val & 15; + envelope_disable[ch] = (val >> 4) & 1; + envelope_loop[ch] = (val >> 5) & 1; + envelope_div_period[ch] = (val & 15); + duty[ch] = (val >> 6) & 3; + break; + + case 0x5002: + case 0x5006: + ch = (adr >> 2) & 1; + freq[ch] = val + (freq[ch] & 0x700); + if (scounter[ch] > freq[ch]) scounter[ch] = freq[ch]; + break; + + case 0x5003: + case 0x5007: + ch = (adr >> 2) & 1; + freq[ch] = (freq[ch] & 0xff) + ((val & 7) << 8); + if (scounter[ch] > freq[ch]) scounter[ch] = freq[ch]; + // phase reset + if (option[OPT_PHASE_REFRESH]) + sphase[ch] = 0; + envelope_write[ch] = true; + if (enable[ch]) + { + length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + break; + + // PCM channel control + case 0x5010: + pcm_mode = ((val & 1) != 0); // 0 = write, 1 = read + break; + + // PCM channel control + case 0x5011: + if (!pcm_mode) + { + val &= 0xFF; + if (val != 0) pcm = val; + } + break; + + case 0x5015: + enable[0] = (val & 1) ? true : false; + enable[1] = (val & 2) ? true : false; + if (!enable[0]) + length_counter[0] = 0; + if (!enable[1]) + length_counter[1] = 0; + break; + + case 0x5205: + mreg[0] = val; + break; + + case 0x5206: + mreg[1] = val; + break; + + default: + return false; + + } + return true; + } + + bool NES_MMC5::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + // in PCM read mode, reads from $8000-$C000 automatically load the PCM output + if (pcm_mode && (0x8000 <= adr) && (adr < 0xC000) && cpu) + { + pcm_mode = false; // prevent recursive entry + UINT32 pcm_read; + cpu->Read(adr, pcm_read); + pcm_read &= 0xFF; + if (pcm_read != 0) + pcm = pcm_read; + pcm_mode = true; + } + + if ((0x5000 <= adr) && (adr < 0x5008)) + { + val = reg[adr&0x7]; + return true; + } + else if(adr == 0x5015) + { + val = (enable[1]?2:0)|(enable[0]?1:0); + return true; + } + + if ((0x5c00 <= adr) && (adr < 0x5ff0)) + { + val = ram[adr & 0x3ff]; + return true; + } + else if (adr == 0x5205) + { + val = (mreg[0] * mreg[1]) & 0xff; + return true; + } + else if (adr == 0x5206) + { + val = (mreg[0] * mreg[1]) >> 8; + return true; + } + + return false; + } + + void NES_MMC5::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_MMC5::GetTrackInfo(int trk) + { + assert(trk<3); + + if (trk < 2) // square + { + trkinfo[trk]._freq = freq[trk]; + if(freq[trk]) + trkinfo[trk].freq = clock/16/(freq[trk] + 1); + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = out[trk]; + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10); + trkinfo[trk].key = (envelope_disable[trk]?(volume[trk]>0): (envelope_counter[trk]>0)); + trkinfo[trk].tone = duty[trk]; + } + else // pcm + { + trkinfo[trk]._freq = 0; + trkinfo[trk].freq = 0; + trkinfo[trk].output = out[2]; + trkinfo[trk].max_volume = 255; + trkinfo[trk].volume = pcm; + trkinfo[trk].key = 0; + trkinfo[trk].tone = pcm_mode ? 1 : 0; + } + + return &trkinfo[trk]; + } + + // pcm read mode requires CPU read access + void NES_MMC5::SetCPU(NES_CPU* cpu_) + { + cpu = cpu_; + } + +}// namespace diff --git a/extern/NSFplay/nes_mmc5.h b/extern/NSFplay/nes_mmc5.h new file mode 100644 index 000000000..b05fbb506 --- /dev/null +++ b/extern/NSFplay/nes_mmc5.h @@ -0,0 +1,74 @@ +#ifndef _NES_MMC5_H_ +#define _NES_MMC5_H_ +#include "../device.h" +#include "../CPU/nes_cpu.h" + +namespace xgm +{ + class NES_MMC5:public ISoundChip + { + public: + enum + { OPT_NONLINEAR_MIXER=0, OPT_PHASE_REFRESH, OPT_END }; + + protected: + int option[OPT_END]; + int mask; + INT32 sm[2][3]; // stereo panning + UINT8 ram[0x6000 - 0x5c00]; + UINT8 reg[8]; + UINT8 mreg[2]; + UINT8 pcm; // PCM channel + bool pcm_mode; // PCM channel + NES_CPU* cpu; // PCM channel reads need CPU access + + UINT32 scounter[2]; // frequency divider + UINT32 sphase[2]; // phase counter + + UINT32 duty[2]; + UINT32 volume[2]; + UINT32 freq[2]; + INT32 out[3]; + bool enable[2]; + + bool envelope_disable[2]; // ƒGƒ“ƒxƒ[ƒv—LŒøƒtƒ‰ƒO + bool envelope_loop[2]; // ƒGƒ“ƒxƒ[ƒvƒ‹[ƒv + bool envelope_write[2]; + int envelope_div_period[2]; + int envelope_div[2]; + int envelope_counter[2]; + + int length_counter[2]; + + int frame_sequence_count; + + double clock, rate; + INT32 calc_sqr (int i, UINT32 clocks); + INT32 square_table[32]; + INT32 pcm_table[256]; + TrackInfoBasic trkinfo[3]; + public: + NES_MMC5 (); + ~NES_MMC5 (); + + void FrameSequence (); + void TickFrameSequence (UINT32 clocks); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 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, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + + void SetCPU(NES_CPU* cpu_); + }; + +} + +#endif diff --git a/extern/NSFplay/nes_n106.cpp b/extern/NSFplay/nes_n106.cpp new file mode 100644 index 000000000..f53d4ae7c --- /dev/null +++ b/extern/NSFplay/nes_n106.cpp @@ -0,0 +1,367 @@ +#include +#include "nes_n106.h" + +namespace xgm { + +NES_N106::NES_N106 () +{ + option[OPT_SERIAL] = 0; + option[OPT_PHASE_READ_ONLY] = 0; + option[OPT_LIMIT_WAVELENGTH] = 0; + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + for (int i=0; i < 8; ++i) + { + sm[0][i] = 128; + sm[1][i] = 128; + } + Reset(); +} + +NES_N106::~NES_N106 () +{ +} + +void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr) +{ + if (trk < 0 || trk >= 8) return; + trk = 7-trk; // displayed channels are inverted + sm[0][trk] = mixl; + sm[1][trk] = mixr; +} + +ITrackInfo *NES_N106::GetTrackInfo (int trk) +{ + int channels = get_channels(); + int channel = 7-trk; // invert the track display + + TrackInfoN106* t = &trkinfo[channel]; + + if (trk >= channels) + { + t->max_volume = 15; + t->volume = 0; + t->_freq = 0; + t->wavelen = 0; + t->tone = -1; + t->output = 0; + t->key = false; + t->freq = 0; + } + else + { + t->max_volume = 15; + t->volume = get_vol(channel); + t->_freq = get_freq(channel); + t->wavelen = get_len(channel); + t->tone = get_off(channel); + t->output = fout[channel]; + + t->key = (t->volume > 0) && (t->_freq > 0); + t->freq = (double(t->_freq) * clock) / double(15 * 65536 * channels * t->wavelen); + + for (int i=0; i < t->wavelen; ++i) + t->wave[i] = get_sample((i+t->tone)&0xFF); + } + + return t; +} + +void NES_N106::SetClock (double c) +{ + clock = c; +} + +void NES_N106::SetRate (double r) +{ + rate = r; +} + +void NES_N106::SetMask (int m) +{ + // bit reverse the mask, + // N163 waves are displayed in reverse order + mask = 0 + | ((m & (1<<0)) ? (1<<7) : 0) + | ((m & (1<<1)) ? (1<<6) : 0) + | ((m & (1<<2)) ? (1<<5) : 0) + | ((m & (1<<3)) ? (1<<4) : 0) + | ((m & (1<<4)) ? (1<<3) : 0) + | ((m & (1<<5)) ? (1<<2) : 0) + | ((m & (1<<6)) ? (1<<1) : 0) + | ((m & (1<<7)) ? (1<<0) : 0); +} + +void NES_N106::SetOption (int id, int val) +{ + if (id 0) + { + int channel = 7-tick_channel; + + UINT32 phase = get_phase(channel); + UINT32 freq = get_freq(channel); + UINT32 len = get_len(channel); + UINT32 off = get_off(channel); + INT32 vol = get_vol(channel); + + // accumulate 24-bit phase + phase = (phase + freq) & 0x00FFFFFF; + + // wrap phase if wavelength exceeded + UINT32 hilen = len << 16; + while (phase >= hilen) phase -= hilen; + + // write back phase + set_phase(phase, channel); + + // fetch sample (note: N163 output is centred at 8, and inverted w.r.t 2A03) + INT32 sample = 8 - get_sample(((phase >> 16) + off) & 0xFF); + fout[channel] = sample * vol; + + // cycle to next channel every 15 clocks + tick_clock -= 15; + ++tick_channel; + if (tick_channel >= channels) + tick_channel = 0; + } +} + +UINT32 NES_N106::Render (INT32 b[2]) +{ + b[0] = 0; + b[1] = 0; + if (master_disable) return 2; + + int channels = get_channels(); + + if (option[OPT_SERIAL]) // hardware accurate serial multiplexing + { + // this could be made more efficient than going clock-by-clock + // but this way is simpler + int clocks = render_clock; + while (clocks > 0) + { + int c = 7-render_channel; + if (0 == ((mask >> c) & 1)) + { + b[0] += fout[c] * sm[0][c]; + b[1] += fout[c] * sm[1][c]; + } + + ++render_subclock; + if (render_subclock >= 15) // each channel gets a 15-cycle slice + { + render_subclock = 0; + ++render_channel; + if (render_channel >= channels) + render_channel = 0; + } + --clocks; + } + + // increase output level by 1 bits (7 bits already added from sm) + b[0] <<= 1; + b[1] <<= 1; + + // average the output + if (render_clock > 0) + { + b[0] /= render_clock; + b[1] /= render_clock; + } + render_clock = 0; + } + else // just mix all channels + { + for (int i = (8-channels); i<8; ++i) + { + if (0 == ((mask >> i) & 1)) + { + b[0] += fout[i] * sm[0][i]; + b[1] += fout[i] * sm[1][i]; + } + } + + // mix together, increase output level by 8 bits, roll off 7 bits from sm + INT32 MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 }; + b[0] = (b[0] * MIX[channels]) >> 7; + b[1] = (b[1] * MIX[channels]) >> 7; + // when approximating the serial multiplex as a straight mix, once the + // multiplex frequency gets below the nyquist frequency an average mix + // begins to sound too quiet. To approximate this effect, I don't attenuate + // any further after 6 channels are active. + } + + // 8 bit approximation of master volume + // max N163 vol vs max APU square + // unfortunately, games have been measured as low as 3.4x and as high as 8.5x + // with higher volumes on Erika, King of Kings, and Rolling Thunder + // and lower volumes on others. Using 6.0x as a rough "one size fits all". + const double MASTER_VOL = 6.0 * 1223.0; + const double MAX_OUT = 15.0 * 15.0 * 256.0; // max digital value + const INT32 GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f); + b[0] = (b[0] * GAIN) >> 8; + b[1] = (b[1] * GAIN) >> 8; + + return 2; +} + +bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id) +{ + if (adr == 0xE000) // master disable + { + master_disable = ((val & 0x40) != 0); + return true; + } + else if (adr == 0xF800) // register select + { + reg_select = (val & 0x7F); + reg_advance = (val & 0x80) != 0; + return true; + } + else if (adr == 0x4800) // register write + { + if (option[OPT_PHASE_READ_ONLY]) // old emulators didn't know phase was stored here + { + int c = 15 - (reg_select/8); + int r = reg_select & 7; + if (c < get_channels() && + (r == 1 || + r == 3 || + r == 5)) + { + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + } + if (option[OPT_LIMIT_WAVELENGTH]) // old emulators ignored top 3 bits of length + { + int c = 15 - (reg_select/8); + int r = reg_select & 7; + if (c < get_channels() && r == 4) + { + val |= 0xE0; + } + } + reg[reg_select] = val; + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + return false; +} + +bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id) +{ + if (adr == 0x4800) // register read + { + val = reg[reg_select]; + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + return false; +} + +// +// register decoding/encoding functions +// + +inline UINT32 NES_N106::get_phase (int channel) +{ + // 24-bit phase stored in channel regs 1/3/5 + channel = channel << 3; + return (reg[0x41 + channel] ) + + (reg[0x43 + channel] << 8 ) + + (reg[0x45 + channel] << 16); +} + +inline UINT32 NES_N106::get_freq (int channel) +{ + // 19-bit frequency stored in channel regs 0/2/4 + channel = channel << 3; + return ( reg[0x40 + channel] ) + + ( reg[0x42 + channel] << 8 ) + + ((reg[0x44 + channel] & 0x03) << 16); +} + +inline UINT32 NES_N106::get_off (int channel) +{ + // 8-bit offset stored in channel reg 6 + channel = channel << 3; + return reg[0x46 + channel]; +} + +inline UINT32 NES_N106::get_len (int channel) +{ + // 6-bit<<3 length stored obscurely in channel reg 4 + channel = channel << 3; + return 256 - (reg[0x44 + channel] & 0xFC); +} + +inline INT32 NES_N106::get_vol (int channel) +{ + // 4-bit volume stored in channel reg 7 + channel = channel << 3; + return reg[0x47 + channel] & 0x0F; +} + +inline INT32 NES_N106::get_sample (UINT32 index) +{ + // every sample becomes 2 samples in regs + return (index&1) ? + ((reg[index>>1] >> 4) & 0x0F) : + ( reg[index>>1] & 0x0F) ; +} + +inline int NES_N106::get_channels () +{ + // 3-bit channel count stored in reg 0x7F + return ((reg[0x7F] >> 4) & 0x07) + 1; +} + +inline void NES_N106::set_phase (UINT32 phase, int channel) +{ + // 24-bit phase stored in channel regs 1/3/5 + channel = channel << 3; + reg[0x41 + channel] = phase & 0xFF; + reg[0x43 + channel] = (phase >> 8 ) & 0xFF; + reg[0x45 + channel] = (phase >> 16) & 0xFF; +} + +} //namespace diff --git a/extern/NSFplay/nes_n106.h b/extern/NSFplay/nes_n106.h new file mode 100644 index 000000000..82d7eaa6a --- /dev/null +++ b/extern/NSFplay/nes_n106.h @@ -0,0 +1,74 @@ +#ifndef _NES_N106_H_ +#define _NES_N106_H_ +#include "../device.h" + +namespace xgm { + +class TrackInfoN106 : public TrackInfoBasic +{ +public: + int wavelen; + INT16 wave[256]; + virtual IDeviceInfo *Clone(){ return new TrackInfoN106(*this); } +}; + +class NES_N106:public ISoundChip +{ +public: + enum + { + OPT_SERIAL = 0, + OPT_PHASE_READ_ONLY = 1, + OPT_LIMIT_WAVELENGTH = 2, + OPT_END + }; + +protected: + double rate, clock; + int mask; + INT32 sm[2][8]; // stereo mix + INT32 fout[8]; // current output + TrackInfoN106 trkinfo[8]; + int option[OPT_END]; + + bool master_disable; + UINT32 reg[0x80]; // all state is contained here + unsigned int reg_select; + bool reg_advance; + int tick_channel; + int tick_clock; + int render_channel; + int render_clock; + int render_subclock; + + // convenience functions to interact with regs + inline UINT32 get_phase (int channel); + inline UINT32 get_freq (int channel); + inline UINT32 get_off (int channel); + inline UINT32 get_len (int channel); + inline INT32 get_vol (int channel); + inline INT32 get_sample (UINT32 index); + inline int get_channels (); + // for storing back the phase after modifying + inline void set_phase (UINT32 phase, int channel); + +public: + NES_N106 (); + ~NES_N106 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 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, INT16 mixl, INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); +}; + +} // namespace xgm + +#endif diff --git a/extern/NSFplay/nes_vrc6.cpp b/extern/NSFplay/nes_vrc6.cpp new file mode 100644 index 000000000..6a19b5ea5 --- /dev/null +++ b/extern/NSFplay/nes_vrc6.cpp @@ -0,0 +1,264 @@ +#include "nes_vrc6.h" + +namespace xgm +{ + + NES_VRC6::NES_VRC6 () + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + + halt = false; + freq_shift = 0; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + NES_VRC6::~NES_VRC6 () + { + } + + void NES_VRC6::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_VRC6::GetTrackInfo(int trk) + { + if(trk<2) + { + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = volume[trk]; + trkinfo[trk]._freq = freq2[trk]; + trkinfo[trk].freq = freq2[trk]?clock/16/(freq2[trk]+1):0; + trkinfo[trk].tone = duty[trk]; + trkinfo[trk].key = (volume[trk]>0)&&enable[trk]&&!gate[trk]; + return &trkinfo[trk]; + } + else if(trk==2) + { + trkinfo[2].max_volume = 255; + trkinfo[2].volume = volume[2]; + trkinfo[2]._freq = freq2[2]; + trkinfo[2].freq = freq2[2]?clock/14/(freq2[2]+1):0; + trkinfo[2].tone = -1; + trkinfo[2].key = (enable[2]>0); + return &trkinfo[2]; + } + else + return NULL; + } + + void NES_VRC6::SetClock (double c) + { + clock = c; + } + + void NES_VRC6::SetRate (double r) + { + rate = r ? r : DEFAULT_RATE; + } + + void NES_VRC6::SetOption (int id, int val) + { + if(id freq2[i]) + { + phase[i] = (phase[i] + 1) & 15; + counter[i] -= (freq2[i] + 1); + } + } + + return (gate[i] + || sqrtbl[duty[i]][phase[i]])? volume[i] : 0; + } + + INT16 NES_VRC6::calc_saw (UINT32 clocks) + { + if (!enable[2]) + return 0; + + if (!halt) + { + counter[2] += clocks; + while(counter[2] > freq2[2]) + { + counter[2] -= (freq2[2] + 1); + + // accumulate saw + ++count14; + if (count14 >= 14) + { + count14 = 0; + phase[2] = 0; + } + else if (0 == (count14 & 1)) // only accumulate on even ticks + { + phase[2] = (phase[2] + volume[2]) & 0xFF; // note 8-bit wrapping behaviour + } + } + } + + // only top 5 bits of saw are output + return phase[2] >> 3; + } + + void NES_VRC6::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0,clocks); + out[1] = calc_sqr(1,clocks); + out[2] = calc_saw(clocks); + } + + UINT32 NES_VRC6::Render (INT32 b[2]) + { + INT32 m[3]; + m[0] = out[0]; + m[1] = out[1]; + m[2] = out[2]; + + // note: signal is inverted compared to 2A03 + + m[0] = (mask & 1) ? 0 : -m[0]; + m[1] = (mask & 2) ? 0 : -m[1]; + m[2] = (mask & 4) ? 0 : -m[2]; + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] += m[2] * sm[0][2]; + //b[0] >>= (7 - 7); + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] += m[2] * sm[1][2]; + //b[1] >>= (7 - 7); + + // master volume adjustment + const INT32 MASTER = INT32(256.0 * 1223.0 / 1920.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; + } + + bool NES_VRC6::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch, cmap[4] = { 0, 0, 1, 2 }; + + switch (adr) + { + case 0x9000: + case 0xa000: + ch = cmap[(adr >> 12) & 3]; + volume[ch] = val & 15; + duty[ch] = (val >> 4) & 7; + gate[ch] = (val >> 7) & 1; + break; + case 0xb000: + volume[2] = val & 63; + break; + + case 0x9001: + case 0xa001: + case 0xb001: + ch = cmap[(adr >> 12) & 3]; + freq[ch] = (freq[ch] & 0xf00) | val; + freq2[ch] = (freq[ch] >> freq_shift); + if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch]; + break; + + case 0x9002: + case 0xa002: + case 0xb002: + ch = cmap[(adr >> 12) & 3]; + freq[ch] = ((val & 0xf) << 8) + (freq[ch] & 0xff); + freq2[ch] = (freq[ch] >> freq_shift); + if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch]; + if (!enable[ch]) // if enable is being turned on, phase should be reset + { + if (ch == 2) + { + count14 = 0; // reset saw + } + phase[ch] = 0; + } + enable[ch] = (val >> 7) & 1; + break; + + case 0x9003: + halt = val & 1; + freq_shift = + (val & 4) ? 8 : + (val & 2) ? 4 : + 0; + freq2[0] = (freq[0] >> freq_shift); + freq2[1] = (freq[1] >> freq_shift); + freq2[2] = (freq[2] >> freq_shift); + if (counter[0] > freq2[0]) counter[0] = freq2[0]; + if (counter[1] > freq2[1]) counter[1] = freq2[1]; + if (counter[2] > freq2[2]) counter[2] = freq2[2]; + break; + + default: + return false; + + } + + return true; + } + + bool NES_VRC6::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + return false; + } + + +} // namespace diff --git a/extern/NSFplay/nes_vrc6.h b/extern/NSFplay/nes_vrc6.h new file mode 100644 index 000000000..2e277e6ee --- /dev/null +++ b/extern/NSFplay/nes_vrc6.h @@ -0,0 +1,56 @@ +#ifndef _NES_VRC6_H_ +#define _NES_VRC6_H_ +#include "../device.h" + +namespace xgm +{ + + class NES_VRC6:public ISoundChip + { + public: + enum + { + OPT_END + }; + protected: + UINT32 counter[3]; // frequency divider + UINT32 phase[3]; // phase counter + UINT32 freq2[3]; // adjusted frequency + int count14; // saw 14-stage counter + + //int option[OPT_END]; + int mask; + INT32 sm[2][3]; // stereo mix + int duty[2]; + int volume[3]; + int enable[3]; + int gate[3]; + UINT32 freq[3]; + INT16 calc_sqr (int i, UINT32 clocks); + INT16 calc_saw (UINT32 clocks); + bool halt; + int freq_shift; + double clock, rate; + INT32 out[3]; + TrackInfoBasic trkinfo[3]; + + public: + NES_VRC6 (); + ~NES_VRC6 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 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, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_vrc7.cpp b/extern/NSFplay/nes_vrc7.cpp new file mode 100644 index 000000000..4123fb36b --- /dev/null +++ b/extern/NSFplay/nes_vrc7.cpp @@ -0,0 +1,189 @@ +#include +#include "nes_vrc7.h" + +namespace xgm +{ + NES_VRC7::NES_VRC7 () + { + use_all_channels = false; + patch_set = OPLL_VRC7_TONE; + patch_custom = NULL; + divider = 0; + + opll = OPLL_new ( 3579545, DEFAULT_RATE); + OPLL_reset_patch (opll, patch_set); + SetClock(DEFAULT_CLOCK); + + for(int c=0;c<2;++c) + //for(int t=0;t<6;++t) + for(int t=0;t<9;++t) // HACK for YM2413 support + sm[c][t] = 128; + } + + NES_VRC7::~NES_VRC7 () + { + OPLL_delete (opll); + } + + void NES_VRC7::UseAllChannels(bool b) + { + use_all_channels = b; + } + + void NES_VRC7::SetPatchSet(int p) + { + patch_set = p; + } + + void NES_VRC7::SetPatchSetCustom (const UINT8* pset) + { + patch_custom = pset; + } + + void NES_VRC7::SetClock (double c) + { + clock = c / 36; + } + + void NES_VRC7::SetRate (double r) + { + //rate = r ? r : DEFAULT_RATE; + (void)r; // rate is ignored + rate = 49716; + OPLL_set_quality(opll, 1); // quality always on (not really a CPU hog) + OPLL_set_rate(opll,(uint32_t)rate); + } + + void NES_VRC7::SetOption (int id, int val) + { + if(id 5) return; + if (trk > 8) return; // HACK YM2413 + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_VRC7::GetTrackInfo(int trk) + { + //if(opll&&trk<6) + if(opll&&trk<9) // HACK YM2413 (percussion mode isn't very diagnostic this way though) + { + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = 15 - ((opll->reg[0x30+trk])&15); + trkinfo[trk]._freq = opll->reg[0x10+trk]+((opll->reg[0x20+trk]&1)<<8); + int blk = (opll->reg[0x20+trk]>>1)&7; + trkinfo[trk].freq = clock*trkinfo[trk]._freq/(double)(0x80000>>blk); + trkinfo[trk].tone = (opll->reg[0x30+trk]>>4)&15; + //trkinfo[trk].key = (opll->reg[0x20+trk]&0x10)?true:false; + trkinfo[trk].key = opll->slot_key_status & (3 << trk)?true:false; + return &trkinfo[trk]; + } + else + return NULL; + } + + bool NES_VRC7::Write (UINT32 adr, UINT32 val, UINT32 id) + { + if (adr == 0x9010) + { + OPLL_writeIO (opll, 0, val); + return true; + } + if (adr == 0x9030) + { + OPLL_writeIO (opll, 1, val); + return true; + } + else + return false; + } + + bool NES_VRC7::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + return false; + } + + void NES_VRC7::Tick (UINT32 clocks) + { + divider += clocks; + while (divider >= 36) + { + divider -= 36; + OPLL_calc(opll); + } + } + + UINT32 NES_VRC7::Render (INT32 b[2]) + { + b[0] = b[1] = 0; + for (int i=0; i < 6; ++i) + { + INT32 val = (mask & (1<ch_out[i] >> 4; + b[0] += val * sm[0][i]; + b[1] += val * sm[1][i]; + } + + // HACK for YM2413 support + if (use_all_channels) + { + for (int i=6; i < 9; ++i) + { + if (mask & (1<patch_number[i] > 15) // rhytm mode + { + if (i == 6) val = opll->ch_out[9]; // BD + else if (i == 7) val = opll->ch_out[10] + opll->ch_out[11]; // HH&SD + else val = opll->ch_out[12] + opll->ch_out[13]; // TOM&CYM + /* (i == 8) is implied */ + } + else + { + val = opll->ch_out[i]; + } + val >>= 4; + b[0] += val * sm[0][i]; + b[1] += val * sm[1][i]; + } + } + + b[0] >>= (7 - 4); + b[1] >>= (7 - 4); + + // master volume adjustment + const INT32 MASTER = INT32(1.15 * 256.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; + } +} diff --git a/extern/NSFplay/nes_vrc7.h b/extern/NSFplay/nes_vrc7.h new file mode 100644 index 000000000..e8a91d61d --- /dev/null +++ b/extern/NSFplay/nes_vrc7.h @@ -0,0 +1,53 @@ +#ifndef _NES_VRC7_H_ +#define _NES_VRC7_H_ +#include "../device.h" +#include "legacy/emu2413.h" + +namespace xgm +{ + + class NES_VRC7 : public ISoundChip + { + public: + enum + { + OPT_OPLL=0, + OPT_END + }; + protected: + int option[OPT_END]; + int mask; + int patch_set; + const UINT8* patch_custom; + //INT32 sm[2][6]; // stereo mix + INT32 sm[2][9]; // stereo mix temporary HACK to support YM2413 + INT16 buf[2]; + OPLL *opll; + UINT32 divider; // clock divider + double clock, rate; + //TrackInfoBasic trkinfo[6]; + TrackInfoBasic trkinfo[9]; // HACK to support YM2413 + bool use_all_channels; + public: + NES_VRC7 (); + ~NES_VRC7 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void UseAllChannels (bool b); + virtual void SetPatchSet (int p); + virtual void SetPatchSetCustom (const UINT8* pset); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetOption (int, int); + virtual void SetMask (int m){ mask = m; if(opll) OPLL_setMask(opll, m); } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif From f6fe93b0edf448d51c4529e3c9eb21fa1257c95b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 22:13:11 -0500 Subject: [PATCH 07/26] convertir de Shift-JIS a UTF-8 por eso es que no deseaba aceptar el uso de NSFplay --- extern/NSFplay/legacy/2413tone.h | 22 - extern/NSFplay/legacy/281btone.h | 20 - extern/NSFplay/legacy/emu2149.c | 363 ------ extern/NSFplay/legacy/emu2149.h | 94 -- extern/NSFplay/legacy/emu2212.c | 485 -------- extern/NSFplay/legacy/emu2212.h | 77 -- extern/NSFplay/legacy/emu2413.c | 1461 ------------------------- extern/NSFplay/legacy/emu2413.h | 247 ----- extern/NSFplay/legacy/emutypes.h | 41 - extern/NSFplay/legacy/vrc7tone_ft35.h | 20 - extern/NSFplay/legacy/vrc7tone_ft36.h | 21 - extern/NSFplay/legacy/vrc7tone_kt1.h | 21 - extern/NSFplay/legacy/vrc7tone_kt2.h | 21 - extern/NSFplay/legacy/vrc7tone_mo.h | 20 - extern/NSFplay/legacy/vrc7tone_nuke.h | 21 - extern/NSFplay/legacy/vrc7tone_rw.h | 21 - extern/NSFplay/nes_apu.cpp | 800 +++++++------- extern/NSFplay/nes_apu.h | 4 +- extern/NSFplay/nes_dmc.cpp | 22 +- extern/NSFplay/nes_dmc.h | 258 ++--- extern/NSFplay/nes_fds.cpp | 2 +- extern/NSFplay/nes_fds.h | 2 +- extern/NSFplay/nes_fme7.cpp | 186 ---- extern/NSFplay/nes_fme7.h | 42 - extern/NSFplay/nes_mmc5.cpp | 2 +- extern/NSFplay/nes_mmc5.h | 6 +- extern/NSFplay/nes_n106.cpp | 2 +- extern/NSFplay/nes_n106.h | 2 +- extern/NSFplay/nes_vrc6.cpp | 2 +- extern/NSFplay/nes_vrc6.h | 2 +- extern/NSFplay/nes_vrc7.cpp | 189 ---- extern/NSFplay/nes_vrc7.h | 53 - 32 files changed, 552 insertions(+), 3977 deletions(-) delete mode 100644 extern/NSFplay/legacy/2413tone.h delete mode 100644 extern/NSFplay/legacy/281btone.h delete mode 100644 extern/NSFplay/legacy/emu2149.c delete mode 100644 extern/NSFplay/legacy/emu2149.h delete mode 100644 extern/NSFplay/legacy/emu2212.c delete mode 100644 extern/NSFplay/legacy/emu2212.h delete mode 100644 extern/NSFplay/legacy/emu2413.c delete mode 100644 extern/NSFplay/legacy/emu2413.h delete mode 100644 extern/NSFplay/legacy/emutypes.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_ft35.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_ft36.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_kt1.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_kt2.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_mo.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_nuke.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_rw.h delete mode 100644 extern/NSFplay/nes_fme7.cpp delete mode 100644 extern/NSFplay/nes_fme7.h delete mode 100644 extern/NSFplay/nes_vrc7.cpp delete mode 100644 extern/NSFplay/nes_vrc7.h diff --git a/extern/NSFplay/legacy/2413tone.h b/extern/NSFplay/legacy/2413tone.h deleted file mode 100644 index 703979584..000000000 --- a/extern/NSFplay/legacy/2413tone.h +++ /dev/null @@ -1,22 +0,0 @@ -/* YM2413 tone by Mitsutaka Okazaki, 2020 */ -/* https://github.com/digital-sound-antiques/emu2413/blob/d2b9c8dfc5e84b7f8a535fdfee0149ac7bd84ca2/emu2413.c#L34 - */ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0: Original -0x71, 0x61, 0x1e, 0x17, 0xd0, 0x78, 0x00, 0x17, // 1: Violin -0x13, 0x41, 0x1a, 0x0d, 0xd8, 0xf7, 0x23, 0x13, // 2: Guitar -0x13, 0x01, 0x99, 0x00, 0xf2, 0xc4, 0x21, 0x23, // 3: Piano -0x11, 0x61, 0x0e, 0x07, 0x8d, 0x64, 0x70, 0x27, // 4: Flute -0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28, // 5: Clarinet -0x31, 0x22, 0x16, 0x05, 0xe0, 0x71, 0x00, 0x18, // 6: Oboe -0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07, // 7: Trumpet -0x33, 0x21, 0x2d, 0x13, 0xb0, 0x70, 0x00, 0x07, // 8: Organ -0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, // 9: Horn -0x41, 0x61, 0x0b, 0x18, 0x85, 0xf0, 0x81, 0x07, // A: Synthesizer -0x33, 0x01, 0x83, 0x11, 0xea, 0xef, 0x10, 0x04, // B: Harpsichord -0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12, // C: Vibraphone -0x61, 0x50, 0x0c, 0x05, 0xd2, 0xf5, 0x40, 0x42, // D: Synthsizer Bass -0x01, 0x01, 0x55, 0x03, 0xe9, 0x90, 0x03, 0x02, // E: Acoustic Bass -0x41, 0x41, 0x89, 0x03, 0xf1, 0xe4, 0xc0, 0x13, // F: Electric Guitar -0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d, // R: Bass Drum (from VRC7) -0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68, // R: High-Hat(M) / Snare Drum(C) (from VRC7) -0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55, // R: Tom-tom(M) / Top Cymbal(C) (from VRC7) diff --git a/extern/NSFplay/legacy/281btone.h b/extern/NSFplay/legacy/281btone.h deleted file mode 100644 index bb0068bab..000000000 --- a/extern/NSFplay/legacy/281btone.h +++ /dev/null @@ -1,20 +0,0 @@ -/* YMF281B tone by Chabin (4/10/2004) */ -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16, -0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23, -0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3, -0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17, -0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, -0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07, -0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17, -0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28, -0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07, -0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11, -0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26, -0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22, -0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4, -0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff, -0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5, -0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, -0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, -0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/emu2149.c b/extern/NSFplay/legacy/emu2149.c deleted file mode 100644 index fa5142f73..000000000 --- a/extern/NSFplay/legacy/emu2149.c +++ /dev/null @@ -1,363 +0,0 @@ -/**************************************************************************** - - emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 - - 2001 04-28 : Version 1.00beta -- 1st Beta Release. - 2001 08-14 : Version 1.10 - 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). - 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. - 2002 10-13 : Version 1.14 -- Fixed the envelope unit. - 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask - 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope - frequency register is written before key-on. - - References: - psg.vhd -- 2000 written by Kazuhiro Tsujikawa. - s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). - ay8910.c -- 1998-2001 Author unknown (MAME). - MSX-Datapack -- 1991 ASCII Corp. - AY-3-8910 data sheet - -*****************************************************************************/ -#include -#include -#include -#include "emu2149.h" - -static e_uint32 voltbl[2][32] = { - {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, - 0x0B, 0x0D, 0x0F, 0x12, - 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, - 0xB4, 0xD6, 0xEB, 0xFF}, - {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, - 0x0B, 0x0B, 0x0F, 0x0F, - 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, - 0xB4, 0xB4, 0xFF, 0xFF} -}; - -#define GETA_BITS 24 - -static void -internal_refresh (PSG * psg) -{ - if (psg->quality) - { - psg->base_incr = 1 << GETA_BITS; - psg->realstep = (e_uint32) ((1 << 31) / psg->rate); - psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 16)); - psg->psgtime = 0; - } - else - { - psg->base_incr = - (e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (16 * psg->rate)); - } -} - -EMU2149_API void -PSG_set_rate (PSG * psg, e_uint32 r) -{ - psg->rate = r ? r : 44100; - internal_refresh (psg); -} - -EMU2149_API void -PSG_set_quality (PSG * psg, e_uint32 q) -{ - psg->quality = q; - internal_refresh (psg); -} - -EMU2149_API PSG * -PSG_new (e_uint32 c, e_uint32 r) -{ - PSG *psg; - - psg = (PSG *) malloc (sizeof (PSG)); - if (psg == NULL) - return NULL; - - PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); - psg->clk = c; - psg->rate = r ? r : 44100; - PSG_set_quality (psg, 0); - - return psg; -} - -EMU2149_API void -PSG_setVolumeMode (PSG * psg, int type) -{ - switch (type) - { - case 1: - psg->voltbl = voltbl[EMU2149_VOL_YM2149]; - break; - case 2: - psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; - break; - default: - psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; - break; - } -} - -EMU2149_API e_uint32 -PSG_setMask (PSG *psg, e_uint32 mask) -{ - e_uint32 ret = 0; - if(psg) - { - ret = psg->mask; - psg->mask = mask; - } - return ret; -} - -EMU2149_API e_uint32 -PSG_toggleMask (PSG *psg, e_uint32 mask) -{ - e_uint32 ret = 0; - if(psg) - { - ret = psg->mask; - psg->mask ^= mask; - } - return ret; -} - -EMU2149_API void -PSG_reset (PSG * psg) -{ - int i; - - psg->base_count = 0; - - for (i = 0; i < 3; i++) - { - psg->cout[i] = 0; - psg->count[i] = 0x1000; - psg->freq[i] = 0; - psg->edge[i] = 0; - psg->volume[i] = 0; - } - - psg->mask = 0; - - for (i = 0; i < 16; i++) - psg->reg[i] = 0; - psg->adr = 0; - - psg->noise_seed = 0xffff; - psg->noise_count = 0x40; - psg->noise_freq = 0; - - psg->env_volume = 0; - psg->env_ptr = 0; - psg->env_freq = 0; - psg->env_count = 0; - psg->env_pause = 1; - - psg->out = 0; -} - -EMU2149_API void -PSG_delete (PSG * psg) -{ - free (psg); -} - -EMU2149_API e_uint8 -PSG_readIO (PSG * psg) -{ - return (e_uint8) (psg->reg[psg->adr]); -} - -EMU2149_API e_uint8 -PSG_readReg (PSG * psg, e_uint32 reg) -{ - return (e_uint8) (psg->reg[reg & 0x1f]); - -} - -EMU2149_API void -PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val) -{ - if (adr & 1) - PSG_writeReg (psg, psg->adr, val); - else - psg->adr = val & 0x1f; -} - -INLINE static e_int16 -calc (PSG * psg) -{ - - int i, noise; - e_uint32 incr; - e_int32 mix = 0; - - psg->base_count += psg->base_incr; - incr = (psg->base_count >> GETA_BITS); - psg->base_count &= (1 << GETA_BITS) - 1; - - /* Envelope */ - psg->env_count += incr; - while (psg->env_count>=0x10000 && psg->env_freq!=0) - { - if (!psg->env_pause) - { - if(psg->env_face) - psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; - else - psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; - } - - if (psg->env_ptr & 0x20) /* if carry or borrow */ - { - if (psg->env_continue) - { - if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; - if (psg->env_hold) psg->env_pause = 1; - psg->env_ptr = psg->env_face?0:0x1f; - } - else - { - psg->env_pause = 1; - psg->env_ptr = 0; - } - } - - psg->env_count -= psg->env_freq; - } - - /* Noise */ - psg->noise_count += incr; - if (psg->noise_count & 0x40) - { - if (psg->noise_seed & 1) - psg->noise_seed ^= 0x24000; - psg->noise_seed >>= 1; - psg->noise_count -= psg->noise_freq; - } - noise = psg->noise_seed & 1; - - /* Tone */ - for (i = 0; i < 3; i++) - { - psg->count[i] += incr; - if (psg->count[i] & 0x1000) - { - if (psg->freq[i] > 1) - { - psg->edge[i] = !psg->edge[i]; - psg->count[i] -= psg->freq[i]; - } - else - { - psg->edge[i] = 1; - } - } - - psg->cout[i] = 0; // maintaining cout for stereo mix - - if (psg->mask&PSG_MASK_CH(i)) - continue; - - if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) - { - if (!(psg->volume[i] & 32)) - psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; - else - psg->cout[i] = psg->voltbl[psg->env_ptr]; - - mix += psg->cout[i]; - } - } - - return (e_int16) mix; -} - -EMU2149_API e_int16 -PSG_calc (PSG * psg) -{ - if (!psg->quality) - return (e_int16) (calc (psg) << 4); - - /* Simple rate converter */ - while (psg->realstep > psg->psgtime) - { - psg->psgtime += psg->psgstep; - psg->out += calc (psg); - psg->out >>= 1; - } - - psg->psgtime = psg->psgtime - psg->realstep; - - return (e_int16) (psg->out << 4); -} - -EMU2149_API void -PSG_writeReg (PSG * psg, e_uint32 reg, e_uint32 val) -{ - int c; - - if (reg > 15) return; - - psg->reg[reg] = (e_uint8) (val & 0xff); - switch (reg) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - c = reg >> 1; - psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; - break; - - case 6: - psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); - break; - - case 7: - psg->tmask[0] = (val & 1); - psg->tmask[1] = (val & 2); - psg->tmask[2] = (val & 4); - psg->nmask[0] = (val & 8); - psg->nmask[1] = (val & 16); - psg->nmask[2] = (val & 32); - break; - - case 8: - case 9: - case 10: - psg->volume[reg - 8] = val << 1; - - break; - - case 11: - case 12: - psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; - break; - - case 13: - psg->env_continue = (val >> 3) & 1; - psg->env_attack = (val >> 2) & 1; - psg->env_alternate = (val >> 1) & 1; - psg->env_hold = val & 1; - psg->env_face = psg->env_attack; - psg->env_pause = 0; - psg->env_count = 0x10000 - psg->env_freq; - psg->env_ptr = psg->env_face?0:0x1f; - break; - - case 14: - case 15: - default: - break; - } - - return; -} \ No newline at end of file diff --git a/extern/NSFplay/legacy/emu2149.h b/extern/NSFplay/legacy/emu2149.h deleted file mode 100644 index a2fba126b..000000000 --- a/extern/NSFplay/legacy/emu2149.h +++ /dev/null @@ -1,94 +0,0 @@ -/* emu2149.h */ -#ifndef _EMU2149_H_ -#define _EMU2149_H_ -#include "emutypes.h" - -#ifdef EMU2149_DLL_EXPORTS -#define EMU2149_API __declspec(dllexport) -#elif EMU2149_DLL_IMPORTS -#define EMU2149_API __declspec(dllimport) -#else -#define EMU2149_API -#endif - -#define EMU2149_VOL_DEFAULT 1 -#define EMU2149_VOL_YM2149 0 -#define EMU2149_VOL_AY_3_8910 1 - -#define PSG_MASK_CH(x) (1<<(x)) - -#ifdef __cplusplus -extern "C" -{ -#endif - - typedef struct __PSG - { - - /* Volume Table */ - e_uint32 *voltbl; - - e_uint8 reg[0x20]; - e_int32 out; - e_int32 cout[3]; - - e_uint32 clk, rate, base_incr, quality; - - e_uint32 count[3]; - e_uint32 volume[3]; - e_uint32 freq[3]; - e_uint32 edge[3]; - e_uint32 tmask[3]; - e_uint32 nmask[3]; - e_uint32 mask; - - e_uint32 base_count; - - e_uint32 env_volume; - e_uint32 env_ptr; - e_uint32 env_face; - - e_uint32 env_continue; - e_uint32 env_attack; - e_uint32 env_alternate; - e_uint32 env_hold; - e_uint32 env_pause; - e_uint32 env_reset; - - e_uint32 env_freq; - e_uint32 env_count; - - e_uint32 noise_seed; - e_uint32 noise_count; - e_uint32 noise_freq; - - /* rate converter */ - e_uint32 realstep; - e_uint32 psgtime; - e_uint32 psgstep; - - /* I/O Ctrl */ - e_uint32 adr; - - } - PSG; - - EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q); - EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r); - EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate); - EMU2149_API void PSG_reset (PSG *); - EMU2149_API void PSG_delete (PSG *); - EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val); - EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val); - EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg); - EMU2149_API e_uint8 PSG_readIO (PSG * psg); - EMU2149_API e_int16 PSG_calc (PSG *); - EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); - EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask); - EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/NSFplay/legacy/emu2212.c b/extern/NSFplay/legacy/emu2212.c deleted file mode 100644 index 88d93382c..000000000 --- a/extern/NSFplay/legacy/emu2212.c +++ /dev/null @@ -1,485 +0,0 @@ -/**************************************************************************** - - emu2212.c -- S.C.C. emulator by Mitsutaka Okazaki 2001 - - 2001 09-30 : Version 1.00 - 2001 10-03 : Version 1.01 -- Added SCC_set_quality(). - 2002 02-14 : Version 1.10 -- Added SCC_writeReg(), SCC_set_type(). - Fixed SCC_write(). - 2002 02-17 : Version 1.11 -- Fixed SCC_write(). - 2002 03-02 : Version 1.12 -- Removed SCC_init & SCC_close. - 2003 09-19 : Version 1.13 -- Added SCC_setMask() and SCC_toggleMask() - 2004 10-21 : Version 1.14 -- Fixed the problem where SCC+ is disabled. - - Registar map for SCC_writeReg() - - $00-1F : WaveTable CH.A - $20-3F : WaveTable CH.B - $40-5F : WaveTable CH.C - $60-7F : WaveTable CH.D&E(SCC), CH.D(SCC+) - $80-9F : WaveTable CH.E - - $C0 : CH.A Freq(L) - $C1 : CH.A Freq(H) - $C2 : CH.B Freq(L) - $C3 : CH.B Freq(H) - $C4 : CH.C Freq(L) - $C5 : CH.C Freq(H) - $C6 : CH.D Freq(L) - $C7 : CH.D Freq(H) - $C8 : CH.E Freq(L) - $C9 : CH.E Freq(H) - - $D0 : CH.A Volume - $D1 : CH.B Volume - $D2 : CH.C Volume - $D3 : CH.D Volume - $D4 : CH.E Volume - - $E0 : Bit0 = 0:SCC, 1:SCC+ - $E1 : CH mask - $E2 : Extra Flags - -*****************************************************************************/ -#include -#include -#include -#include "emu2212.h" - -#define GETA_BITS 22 - -static void -internal_refresh (SCC * scc) -{ - if (scc->quality) - { - scc->base_incr = 2 << GETA_BITS; - scc->realstep = (e_uint32) ((1 << 31) / scc->rate); - scc->sccstep = (e_uint32) ((1 << 31) / (scc->clk / 2)); - scc->scctime = 0; - } - else - { - scc->base_incr = (e_uint32) ((double) scc->clk * (1 << GETA_BITS) / scc->rate); - } -} - -EMU2212_API e_uint32 -SCC_setMask (SCC *scc, e_uint32 mask) -{ - e_uint32 ret = 0; - if(scc) - { - ret = scc->mask; - scc->mask = mask; - } - return ret; -} - -EMU2212_API e_uint32 -SCC_toggleMask (SCC *scc, e_uint32 mask) -{ - e_uint32 ret = 0; - if(scc) - { - ret = scc->mask; - scc->mask ^= mask; - } - return ret; -} - -EMU2212_API void -SCC_set_quality (SCC * scc, e_uint32 q) -{ - scc->quality = q; - internal_refresh (scc); -} - -EMU2212_API void -SCC_set_rate (SCC * scc, e_uint32 r) -{ - scc->rate = r ? r : 44100; - internal_refresh (scc); -} - -EMU2212_API SCC * -SCC_new (e_uint32 c, e_uint32 r) -{ - SCC *scc; - - scc = (SCC *) malloc (sizeof (SCC)); - if (scc == NULL) - return NULL; - memset(scc, 0, sizeof (SCC)); - - scc->clk = c; - scc->rate = r ? r : 44100; - SCC_set_quality (scc, 0); - scc->type = SCC_ENHANCED; - return scc; -} - -EMU2212_API void -SCC_reset (SCC * scc) -{ - int i, j; - - if (scc == NULL) - return; - - scc->mode = 0; - scc->active = 0; - scc->base_adr = 0x9000; - - for (i = 0; i < 5; i++) - { - for (j = 0; j < 5; j++) - scc->wave[i][j] = 0; - scc->count[i] = 0; - scc->freq[i] = 0; - scc->phase[i] = 0; - scc->volume[i] = 0; - scc->offset[i] = 0; - scc->rotate[i] = 0; - } - - memset(scc->reg,0,0x100-0xC0); - - scc->mask = 0; - - scc->ch_enable = 0xff; - scc->ch_enable_next = 0xff; - - scc->cycle_4bit = 0; - scc->cycle_8bit = 0; - scc->refresh = 0; - - scc->out = 0; - scc->prev = 0; - scc->next = 0; - - return; -} - -EMU2212_API void -SCC_delete (SCC * scc) -{ - if (scc != NULL) - free (scc); -} - -INLINE static e_int16 -calc (SCC * scc) -{ - int i; - e_int32 mix = 0; - - for (i = 0; i < 5; i++) - { - scc->count[i] = (scc->count[i] + scc->incr[i]); - - if (scc->count[i] & (1 << (GETA_BITS + 5))) - { - scc->count[i] &= ((1 << (GETA_BITS + 5)) - 1); - scc->offset[i] = (scc->offset[i] + 31) & scc->rotate[i]; - scc->ch_enable &= ~(1 << i); - scc->ch_enable |= scc->ch_enable_next & (1 << i); - } - - if (scc->ch_enable & (1 << i)) - { - scc->phase[i] = ((scc->count[i] >> (GETA_BITS)) + scc->offset[i]) & 0x1F; - if(!(scc->mask&SCC_MASK_CH(i))) - mix += ((((e_int8) (scc->wave[i][scc->phase[i]]) * (e_int8) scc->volume[i]))) >> 4; - } - } - - return (e_int16) (mix << 4); -} - -EMU2212_API e_int16 -SCC_calc (SCC * scc) -{ - if (!scc->quality) - return calc (scc); - - while (scc->realstep > scc->scctime) - { - scc->scctime += scc->sccstep; - scc->prev = scc->next; - scc->next = calc (scc); - } - - scc->scctime -= scc->realstep; - scc->out = (e_int16) (((double) scc->next * (scc->sccstep - scc->scctime) + (double) scc->prev * scc->scctime) / scc->sccstep); - - return (e_int16) (scc->out); -} - -EMU2212_API e_uint32 -SCC_readReg (SCC * scc, e_uint32 adr) -{ - if (adr < 0xA0) - return scc->wave[adr >> 5][adr & 0x1f]; - else if( 0xC0 < adr && adr < 0xF0 ) - return scc->reg[adr-0xC0]; - else - return 0; -} - -EMU2212_API void -SCC_writeReg (SCC * scc, e_uint32 adr, e_uint32 val) -{ - int ch; - e_uint32 freq; - - adr &= 0xFF; - - if (adr < 0xA0) - { - ch = (adr & 0xF0) >> 5; - if (!scc->rotate[ch]) - { - scc->wave[ch][adr & 0x1F] = (e_int8) val; - if (scc->mode == 0 && ch == 3) - scc->wave[4][adr & 0x1F] = (e_int8) val; - } - } - else if (0xC0 <= adr && adr <= 0xC9) - { - scc->reg[adr-0xC0] = val; - ch = (adr & 0x0F) >> 1; - if (adr & 1) - scc->freq[ch] = ((val & 0xF) << 8) | (scc->freq[ch] & 0xFF); - else - scc->freq[ch] = (scc->freq[ch] & 0xF00) | (val & 0xFF); - - if (scc->refresh) - scc->count[ch] = 0; - freq = scc->freq[ch]; - if (scc->cycle_8bit) - freq &= 0xFF; - if (scc->cycle_4bit) - freq >>= 8; - if (freq <= 8) - scc->incr[ch] = 0; - else - scc->incr[ch] = scc->base_incr / (freq + 1); - } - else if (0xD0 <= adr && adr <= 0xD4) - { - scc->reg[adr-0xC0] = val; - scc->volume[adr & 0x0F] = (e_uint8) (val & 0xF); - } - else if (adr == 0xE0) - { - scc->reg[adr-0xC0] = val; - scc->mode = (e_uint8) val & 1; - } - else if (adr == 0xE1) - { - scc->reg[adr-0xC0] = val; - scc->ch_enable_next = (e_uint8) val & 0x1F; - } - else if (adr == 0xE2) - { - scc->reg[adr-0xC0] = val; - scc->cycle_4bit = val & 1; - scc->cycle_8bit = val & 2; - scc->refresh = val & 32; - if (val & 64) - for (ch = 0; ch < 5; ch++) - scc->rotate[ch] = 0x1F; - else - for (ch = 0; ch < 5; ch++) - scc->rotate[ch] = 0; - if (val & 128) - scc->rotate[3] = scc->rotate[4] = 0x1F; - } - - return; -} - -INLINE static void -write_standard (SCC * scc, e_uint32 adr, e_uint32 val) -{ - adr &= 0xFF; - - if (adr < 0x80) /* wave */ - { - SCC_writeReg (scc, adr, val); - } - else if (adr < 0x8A) /* freq */ - { - SCC_writeReg (scc, adr + 0xC0 - 0x80, val); - } - else if (adr < 0x8F) /* volume */ - { - SCC_writeReg (scc, adr + 0xD0 - 0x8A, val); - } - else if (adr == 0x8F) /* ch enable */ - { - SCC_writeReg (scc, 0xE1, val); - } - else if (0xE0 <= adr) /* flags */ - { - SCC_writeReg (scc, 0xE2, val); - } -} - -INLINE static void -write_enhanced (SCC * scc, e_uint32 adr, e_uint32 val) -{ - adr &= 0xFF; - - if (adr < 0xA0) /* wave */ - { - SCC_writeReg (scc, adr, val); - } - else if (adr < 0xAA) /* freq */ - { - SCC_writeReg (scc, adr + 0xC0 - 0xA0, val); - } - else if (adr < 0xAF) /* volume */ - { - SCC_writeReg (scc, adr + 0xD0 - 0xAA, val); - } - else if (adr == 0xAF) /* ch enable */ - { - SCC_writeReg (scc, 0xE1, val); - } - else if (0xC0 <= adr && adr <= 0xDF) /* flags */ - { - SCC_writeReg (scc, 0xE2, val); - } -} - -INLINE static e_uint32 -read_enhanced (SCC * scc, e_uint32 adr) -{ - adr &= 0xFF; - if (adr < 0xA0) - return SCC_readReg (scc, adr); - else if (adr < 0xAA) - return SCC_readReg (scc, adr + 0xC0 - 0xA0); - else if (adr < 0xAF) - return SCC_readReg (scc, adr + 0xD0 - 0xAA); - else if (adr == 0xAF) - return SCC_readReg (scc, 0xE1); - else if (0xC0 <= adr && adr <= 0xDF) - return SCC_readReg (scc, 0xE2); - else - return 0; -} - -INLINE static e_uint32 -read_standard (SCC * scc, e_uint32 adr) -{ - adr &= 0xFF; - if(adr<0x80) - return SCC_readReg (scc, adr); - else if (0xA0<=adr&&adr<=0xBF) - return SCC_readReg (scc, 0x80+(adr&0x1F)); - else if (adr < 0x8A) - return SCC_readReg (scc, adr + 0xC0 - 0x80); - else if (adr < 0x8F) - return SCC_readReg (scc, adr + 0xD0 - 0x8A); - else if (adr == 0x8F) - return SCC_readReg (scc, 0xE1); - else if (0xE0 <= adr) - return SCC_readReg (scc, 0xE2); - else return 0; -} - -EMU2212_API e_uint32 -SCC_read (SCC * scc, e_uint32 adr) -{ - if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) - return (scc->base_adr>>8)&0x20; - - if( adr < scc->base_adr ) return 0; - adr -= scc->base_adr; - - if( adr == 0 ) - { - if(scc->mode) return 0x80; else return 0x3F; - } - - if(!scc->active||adr<0x800||0x8FFtype) - { - case SCC_STANDARD: - return read_standard (scc, adr); - break; - case SCC_ENHANCED: - if(!scc->mode) - return read_standard (scc, adr); - else - return read_enhanced (scc, adr); - break; - default: - break; - } - - return 0; -} - -EMU2212_API void -SCC_write (SCC * scc, e_uint32 adr, e_uint32 val) -{ - val = val & 0xFF; - - if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) - { - scc->base_adr = 0x9000 | ((val&0x20)<<8); - return; - } - - if( adr < scc->base_adr ) return; - adr -= scc->base_adr; - - if(adr == 0) - { - if( val == 0x3F ) - { - scc->mode = 0; - scc->active = 1; - } - else if( val&0x80 && scc->type == SCC_ENHANCED) - { - scc->mode = 1; - scc->active = 1; - } - else - { - scc->mode = 0; - scc->active = 0; - } - return; - } - - if(!scc->active||adr<0x800||0x8FFtype) - { - case SCC_STANDARD: - write_standard (scc, adr, val); - break; - case SCC_ENHANCED: - if(scc->mode) - write_enhanced (scc, adr, val); - else - write_standard (scc, adr, val); - default: - break; - } - - return; -} - -EMU2212_API void -SCC_set_type (SCC * scc, e_uint32 type) -{ - scc->type = type; -} diff --git a/extern/NSFplay/legacy/emu2212.h b/extern/NSFplay/legacy/emu2212.h deleted file mode 100644 index b79e0c360..000000000 --- a/extern/NSFplay/legacy/emu2212.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _EMU2212_H_ -#define _EMU2212_H_ - -#ifdef EMU2212_DLL_EXPORTS - #define EMU2212_API __declspec(dllexport) -#elif defined(EMU2212_DLL_IMPORTS) - #define EMU2212_API __declspec(dllimport) -#else - #define EMU2212_API -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include "emutypes.h" - -#define SCC_STANDARD 0 -#define SCC_ENHANCED 1 - -#define SCC_MASK_CH(x) (1<<(x)) - -typedef struct __SCC { - - e_uint32 clk, rate ,base_incr, quality ; - - e_int32 out, prev, next; - e_uint32 type ; - e_uint32 mode ; - e_uint32 active; - e_uint32 base_adr; - e_uint32 mask ; - - e_uint32 realstep ; - e_uint32 scctime ; - e_uint32 sccstep ; - - e_uint32 incr[5] ; - - e_int8 wave[5][32] ; - - e_uint32 count[5] ; - e_uint32 freq[5] ; - e_uint32 phase[5] ; - e_uint32 volume[5] ; - e_uint32 offset[5] ; - e_uint8 reg[0x100-0xC0]; - - int ch_enable ; - int ch_enable_next ; - - int cycle_4bit ; - int cycle_8bit ; - int refresh ; - int rotate[5] ; - -} SCC ; - - -EMU2212_API SCC *SCC_new(e_uint32 c, e_uint32 r) ; -EMU2212_API void SCC_reset(SCC *scc) ; -EMU2212_API void SCC_set_rate(SCC *scc, e_uint32 r); -EMU2212_API void SCC_set_quality(SCC *scc, e_uint32 q) ; -EMU2212_API void SCC_set_type(SCC *scc, e_uint32 type) ; -EMU2212_API void SCC_delete(SCC *scc) ; -EMU2212_API e_int16 SCC_calc(SCC *scc) ; -EMU2212_API void SCC_write(SCC *scc, e_uint32 adr, e_uint32 val) ; -EMU2212_API void SCC_writeReg(SCC *scc, e_uint32 adr, e_uint32 val) ; -EMU2212_API e_uint32 SCC_read(SCC *scc, e_uint32 adr) ; -EMU2212_API e_uint32 SCC_setMask(SCC *scc, e_uint32 adr) ; -EMU2212_API e_uint32 SCC_toggleMask(SCC *scc, e_uint32 adr) ; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/NSFplay/legacy/emu2413.c b/extern/NSFplay/legacy/emu2413.c deleted file mode 100644 index 2ced98f86..000000000 --- a/extern/NSFplay/legacy/emu2413.c +++ /dev/null @@ -1,1461 +0,0 @@ -/** - * emu2413 v1.5.2 - * https://github.com/digital-sound-antiques/emu2413 - * Copyright (C) 2020 Mitsutaka Okazaki - * - * This source refers to the following documents. The author would like to thank all the authors who have - * contributed to the writing of them. - * - [YM2413 notes](http://www.smspower.org/Development/YM2413) by andete - * - ymf262.c by Jarek Burczynski - * - [VRC7 presets](https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#opll_vrc7_patch_format) by Nuke.YKT - * - YMF281B presets by Chabin - */ -#include "emu2413.h" -#include -#include -#include -#include - -#ifndef INLINE -#if defined(_MSC_VER) -#define INLINE __inline -#elif defined(__GNUC__) -#define INLINE __inline__ -#else -#define INLINE inline -#endif -#endif - -#define _PI_ 3.14159265358979323846264338327950288 - -#define OPLL_TONE_NUM 9 -static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 8] = { - { -#include "vrc7tone_nuke.h" - }, - { -#include "vrc7tone_rw.h" - }, - { -#include "vrc7tone_ft36.h" - }, - { -#include "vrc7tone_ft35.h" - }, - { -#include "vrc7tone_mo.h" - }, - { -#include "vrc7tone_kt2.h" - }, - { -#include "vrc7tone_kt1.h" - }, - { -#include "2413tone.h" - }, - { -#include "281btone.h" - }, -}; - -/* phase increment counter */ -#define DP_BITS 19 -#define DP_WIDTH (1 << DP_BITS) -#define DP_BASE_BITS (DP_BITS - PG_BITS) - -/* dynamic range of envelope output */ -#define EG_STEP 0.375 -#define EG_BITS 7 -#define EG_MUTE ((1 << EG_BITS) - 1) -#define EG_MAX (EG_MUTE - 3) - -/* dynamic range of total level */ -#define TL_STEP 0.75 -#define TL_BITS 6 - -/* dynamic range of sustine level */ -#define SL_STEP 3.0 -#define SL_BITS 4 - -/* damper speed before key-on. key-scale affects. */ -#define DAMPER_RATE 12 - -#define TL2EG(d) ((d) << 1) - -/* sine table */ -#define PG_BITS 10 /* 2^10 = 1024 length sine table */ -#define PG_WIDTH (1 << PG_BITS) - -/* clang-format off */ -/* exp_table[x] = round((exp2((double)x / 256.0) - 1) * 1024) */ -static uint16_t exp_table[256] = { -0, 3, 6, 8, 11, 14, 17, 20, 22, 25, 28, 31, 34, 37, 40, 42, -45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, -93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 130, 133, 136, 139, -142, 145, 148, 152, 155, 158, 161, 164, 168, 171, 174, 177, 181, 184, 187, 190, -194, 197, 200, 204, 207, 210, 214, 217, 220, 224, 227, 231, 234, 237, 241, 244, -248, 251, 255, 258, 262, 265, 268, 272, 276, 279, 283, 286, 290, 293, 297, 300, -304, 308, 311, 315, 318, 322, 326, 329, 333, 337, 340, 344, 348, 352, 355, 359, -363, 367, 370, 374, 378, 382, 385, 389, 393, 397, 401, 405, 409, 412, 416, 420, -424, 428, 432, 436, 440, 444, 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, -488, 492, 496, 501, 505, 509, 513, 517, 521, 526, 530, 534, 538, 542, 547, 551, -555, 560, 564, 568, 572, 577, 581, 585, 590, 594, 599, 603, 607, 612, 616, 621, -625, 630, 634, 639, 643, 648, 652, 657, 661, 666, 670, 675, 680, 684, 689, 693, -698, 703, 708, 712, 717, 722, 726, 731, 736, 741, 745, 750, 755, 760, 765, 770, -774, 779, 784, 789, 794, 799, 804, 809, 814, 819, 824, 829, 834, 839, 844, 849, -854, 859, 864, 869, 874, 880, 885, 890, 895, 900, 906, 911, 916, 921, 927, 932, -937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018 -}; -/* fullsin_table[x] = round(-log2(sin((x + 0.5) * PI / (PG_WIDTH / 4) / 2)) * 256) */ -static uint16_t fullsin_table[PG_WIDTH] = { -2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, -846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, -598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, -453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, -352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, -276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, -215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, -167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, -127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, -94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, -67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, -46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, -29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, -16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, -7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, -2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, -}; -/* clang-format on */ - -static uint16_t halfsin_table[PG_WIDTH]; -static uint16_t *wave_table_map[2] = {fullsin_table, halfsin_table}; - -/* pitch modulator */ -/* offset to fnum, rough approximation of 14 cents depth. */ -static int8_t pm_table[8][8] = { - {0, 0, 0, 0, 0, 0, 0, 0}, // fnum = 000xxxxxx - {0, 0, 1, 0, 0, 0, -1, 0}, // fnum = 001xxxxxx - {0, 1, 2, 1, 0, -1, -2, -1}, // fnum = 010xxxxxx - {0, 1, 3, 1, 0, -1, -3, -1}, // fnum = 011xxxxxx - {0, 2, 4, 2, 0, -2, -4, -2}, // fnum = 100xxxxxx - {0, 2, 5, 2, 0, -2, -5, -2}, // fnum = 101xxxxxx - {0, 3, 6, 3, 0, -3, -6, -3}, // fnum = 110xxxxxx - {0, 3, 7, 3, 0, -3, -7, -3}, // fnum = 111xxxxxx -}; - -/* amplitude lfo table */ -/* The following envelop pattern is verified on real YM2413. */ -/* each element repeates 64 cycles */ -static uint8_t am_table[210] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // - 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // - 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, // - 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, // - 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, // - 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, // - 12, 12, 12, 12, 12, 12, 12, 12, // - 13, 13, 13, // - 12, 12, 12, 12, 12, 12, 12, 12, // - 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, // - 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, // - 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, // - 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // - 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, // - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; - -/* envelope decay increment step table */ -/* based on andete's research */ -static uint8_t eg_step_tables[4][8] = { - {0, 1, 0, 1, 0, 1, 0, 1}, - {0, 1, 0, 1, 1, 1, 0, 1}, - {0, 1, 1, 1, 0, 1, 1, 1}, - {0, 1, 1, 1, 1, 1, 1, 1}, -}; - -enum __OPLL_EG_STATE { ATTACK, DECAY, SUSTAIN, RELEASE, DAMP, UNKNOWN }; - -static uint32_t ml_table[16] = {1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, - 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2}; - -#define dB2(x) ((x)*2) -static double kl_table[16] = {dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125), - dB2(16.875), dB2(17.625), dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500), - dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000)}; - -static uint32_t tll_table[8 * 16][1 << TL_BITS][4]; -static int32_t rks_table[8 * 2][2]; - -static OPLL_PATCH null_patch = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2]; - -#define min(i, j) (((i) < (j)) ? (i) : (j)) -#define max(i, j) (((i) > (j)) ? (i) : (j)) - -/*************************************************** - - Internal Sample Rate Converter - -****************************************************/ -/* Note: to disable internal rate converter, set clock/72 to output sampling rate. */ - -/* - * LW is truncate length of sinc(x) calculation. - * Lower LW is faster, higher LW results better quality. - * LW must be a non-zero positive even number, no upper limit. - * LW=16 or greater is recommended when upsampling. - * LW=8 is practically okay for downsampling. - */ -#define LW 16 - -/* resolution of sinc(x) table. sinc(x) where 0.0<=x<1.0 corresponds to sinc_table[0...SINC_RESO-1] */ -#define SINC_RESO 256 -#define SINC_AMP_BITS 12 - -// double hamming(double x) { return 0.54 - 0.46 * cos(2 * PI * x); } -static double blackman(double x) { return 0.42 - 0.5 * cos(2 * _PI_ * x) + 0.08 * cos(4 * _PI_ * x); } -static double sinc(double x) { return (x == 0.0 ? 1.0 : sin(_PI_ * x) / (_PI_ * x)); } -static double windowed_sinc(double x) { return blackman(0.5 + 0.5 * x / (LW / 2)) * sinc(x); } - -/* f_inp: input frequency. f_out: output frequencey, ch: number of channels */ -OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch) { - OPLL_RateConv *conv = malloc(sizeof(OPLL_RateConv)); - int i; - - conv->ch = ch; - conv->f_ratio = f_inp / f_out; - conv->buf = malloc(sizeof(void *) * ch); - for (i = 0; i < ch; i++) { - conv->buf[i] = malloc(sizeof(conv->buf[0][0]) * LW); - } - - /* create sinc_table for positive 0 <= x < LW/2 */ - conv->sinc_table = malloc(sizeof(conv->sinc_table[0]) * SINC_RESO * LW / 2); - for (i = 0; i < SINC_RESO * LW / 2; i++) { - const double x = (double)i / SINC_RESO; - if (f_out < f_inp) { - /* for downsampling */ - conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x / conv->f_ratio) / conv->f_ratio); - } else { - /* for upsampling */ - conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x)); - } - } - - return conv; -} - -static INLINE int16_t lookup_sinc_table(int16_t *table, double x) { - int16_t index = (int16_t)(x * SINC_RESO); - if (index < 0) - index = -index; - return table[min(SINC_RESO * LW / 2 - 1, index)]; -} - -void OPLL_RateConv_reset(OPLL_RateConv *conv) { - int i; - conv->timer = 0; - for (i = 0; i < conv->ch; i++) { - memset(conv->buf[i], 0, sizeof(conv->buf[i][0]) * LW); - } -} - -/* put original data to this converter at f_inp. */ -void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data) { - int16_t *buf = conv->buf[ch]; - int i; - for (i = 0; i < LW - 1; i++) { - buf[i] = buf[i + 1]; - } - buf[LW - 1] = data; -} - -/* get resampled data from this converter at f_out. */ -/* this function must be called f_out / f_inp times per one putData call. */ -int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch) { - int16_t *buf = conv->buf[ch]; - int32_t sum = 0; - int k; - double dn; - conv->timer += conv->f_ratio; - dn = conv->timer - floor(conv->timer); - conv->timer = dn; - - for (k = 0; k < LW; k++) { - double x = ((double)k - (LW / 2 - 1)) - dn; - sum += buf[k] * lookup_sinc_table(conv->sinc_table, x); - } - return sum >> SINC_AMP_BITS; -} - -void OPLL_RateConv_delete(OPLL_RateConv *conv) { - int i; - for (i = 0; i < conv->ch; i++) { - free(conv->buf[i]); - } - free(conv->buf); - free(conv->sinc_table); - free(conv); -} - -/*************************************************** - - Create tables - -****************************************************/ - -static void makeSinTable(void) { - int x; - - for (x = 0; x < PG_WIDTH / 4; x++) { - fullsin_table[PG_WIDTH / 4 + x] = fullsin_table[PG_WIDTH / 4 - x - 1]; - } - - for (x = 0; x < PG_WIDTH / 2; x++) { - fullsin_table[PG_WIDTH / 2 + x] = 0x8000 | fullsin_table[x]; - } - - for (x = 0; x < PG_WIDTH / 2; x++) - halfsin_table[x] = fullsin_table[x]; - - for (x = PG_WIDTH / 2; x < PG_WIDTH; x++) - halfsin_table[x] = 0xfff; -} - -static void makeTllTable(void) { - - int32_t tmp; - int32_t fnum, block, TL, KL; - - for (fnum = 0; fnum < 16; fnum++) { - for (block = 0; block < 8; block++) { - for (TL = 0; TL < 64; TL++) { - for (KL = 0; KL < 4; KL++) { - if (KL == 0) { - tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); - } else { - tmp = (int32_t)(kl_table[fnum] - dB2(3.000) * (7 - block)); - if (tmp <= 0) - tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); - else - tll_table[(block << 4) | fnum][TL][KL] = (uint32_t)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); - } - } - } - } - } -} - -static void makeRksTable(void) { - int fnum8, block; - for (fnum8 = 0; fnum8 < 2; fnum8++) - for (block = 0; block < 8; block++) { - rks_table[(block << 1) | fnum8][1] = (block << 1) + fnum8; - rks_table[(block << 1) | fnum8][0] = block >> 1; - } -} - -static void makeDefaultPatch() { - int i, j; - for (i = 0; i < OPLL_TONE_NUM; i++) - for (j = 0; j < 19; j++) - OPLL_getDefaultPatch(i, j, &default_patch[i][j * 2]); -} - -static uint8_t table_initialized = 0; - -static void initializeTables() { - makeTllTable(); - makeRksTable(); - makeSinTable(); - makeDefaultPatch(); - table_initialized = 1; -} - -/********************************************************* - - Synthesizing - -*********************************************************/ -#define SLOT_BD1 12 -#define SLOT_BD2 13 -#define SLOT_HH 14 -#define SLOT_SD 15 -#define SLOT_TOM 16 -#define SLOT_CYM 17 - -/* utility macros */ -#define MOD(o, x) (&(o)->slot[(x) << 1]) -#define CAR(o, x) (&(o)->slot[((x) << 1) | 1]) -#define BIT(s, b) (((s) >> (b)) & 1) - -#if OPLL_DEBUG -static void _debug_print_patch(OPLL_SLOT *slot) { - OPLL_PATCH *p = slot->patch; - printf("[slot#%d am:%d pm:%d eg:%d kr:%d ml:%d kl:%d tl:%d ws:%d fb:%d A:%d D:%d S:%d R:%d]\n", slot->number, // - p->AM, p->PM, p->EG, p->KR, p->ML, // - p->KL, p->TL, p->WS, p->FB, // - p->AR, p->DR, p->SL, p->RR); -} - -static char *_debug_eg_state_name(OPLL_SLOT *slot) { - switch (slot->eg_state) { - case ATTACK: - return "attack"; - case DECAY: - return "decay"; - case SUSTAIN: - return "sustain"; - case RELEASE: - return "release"; - case DAMP: - return "damp"; - default: - return "unknown"; - } -} - -static INLINE void _debug_print_slot_info(OPLL_SLOT *slot) { - char *name = _debug_eg_state_name(slot); - printf("[slot#%d state:%s fnum:%03x rate:%d-%d]\n", slot->number, name, slot->blk_fnum, slot->eg_rate_h, - slot->eg_rate_l); - _debug_print_patch(slot); - fflush(stdout); -} -#endif - -static INLINE int get_parameter_rate(OPLL_SLOT *slot) { - - if ((slot->type & 1) == 0 && slot->key_flag == 0) { - return 0; - } - - switch (slot->eg_state) { - case ATTACK: - return slot->patch->AR; - case DECAY: - return slot->patch->DR; - case SUSTAIN: - return slot->patch->EG ? 0 : slot->patch->RR; - case RELEASE: - if (slot->sus_flag) { - return 5; - } else if (slot->patch->EG) { - return slot->patch->RR; - } else { - return 7; - } - case DAMP: - return DAMPER_RATE; - default: - return 0; - } -} - -enum SLOT_UPDATE_FLAG { - UPDATE_WS = 1, - UPDATE_TLL = 2, - UPDATE_RKS = 4, - UPDATE_EG = 8, - UPDATE_ALL = 255, -}; - -static INLINE void request_update(OPLL_SLOT *slot, int flag) { slot->update_requests |= flag; } - -static void commit_slot_update(OPLL_SLOT *slot) { - -#if OPLL_DEBUG - if (slot->last_eg_state != slot->eg_state) { - _debug_print_slot_info(slot); - slot->last_eg_state = slot->eg_state; - } -#endif - - if (slot->update_requests & UPDATE_WS) { - slot->wave_table = wave_table_map[slot->patch->WS]; - } - - if (slot->update_requests & UPDATE_TLL) { - if ((slot->type & 1) == 0) { - slot->tll = tll_table[slot->blk_fnum >> 5][slot->patch->TL][slot->patch->KL]; - } else { - slot->tll = tll_table[slot->blk_fnum >> 5][slot->volume][slot->patch->KL]; - } - } - - if (slot->update_requests & UPDATE_RKS) { - slot->rks = rks_table[slot->blk_fnum >> 8][slot->patch->KR]; - } - - if (slot->update_requests & (UPDATE_RKS | UPDATE_EG)) { - int p_rate = get_parameter_rate(slot); - - if (p_rate == 0) { - slot->eg_shift = 0; - slot->eg_rate_h = 0; - slot->eg_rate_l = 0; - return; - } - - slot->eg_rate_h = min(15, p_rate + (slot->rks >> 2)); - slot->eg_rate_l = slot->rks & 3; - if (slot->eg_state == ATTACK) { - slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (13 - slot->eg_rate_h) : 0; - } else { - slot->eg_shift = (slot->eg_rate_h < 13) ? (13 - slot->eg_rate_h) : 0; - } - } - - slot->update_requests = 0; -} - -static void reset_slot(OPLL_SLOT *slot, int number) { - slot->number = number; - slot->type = number % 2; - slot->pg_keep = 0; - slot->wave_table = wave_table_map[0]; - slot->pg_phase = 0; - slot->output[0] = 0; - slot->output[1] = 0; - slot->eg_state = RELEASE; - slot->eg_shift = 0; - slot->rks = 0; - slot->tll = 0; - slot->key_flag = 0; - slot->sus_flag = 0; - slot->blk_fnum = 0; - slot->blk = 0; - slot->fnum = 0; - slot->volume = 0; - slot->pg_out = 0; - slot->eg_out = EG_MUTE; - slot->patch = &null_patch; -} - -static INLINE void slotOn(OPLL *opll, int i) { - OPLL_SLOT *slot = &opll->slot[i]; - slot->key_flag = 1; - slot->eg_state = DAMP; - request_update(slot, UPDATE_EG); -} - -static INLINE void slotOff(OPLL *opll, int i) { - OPLL_SLOT *slot = &opll->slot[i]; - slot->key_flag = 0; - if (slot->type & 1) { - slot->eg_state = RELEASE; - request_update(slot, UPDATE_EG); - } -} - -static INLINE void update_key_status(OPLL *opll) { - const uint8_t r14 = opll->reg[0x0e]; - const uint8_t rhythm_mode = BIT(r14, 5); - uint32_t new_slot_key_status = 0; - uint32_t updated_status; - int ch; - - for (ch = 0; ch < 9; ch++) - if (opll->reg[0x20 + ch] & 0x10) - new_slot_key_status |= 3 << (ch * 2); - - if (rhythm_mode) { - if (r14 & 0x10) - new_slot_key_status |= 3 << SLOT_BD1; - - if (r14 & 0x01) - new_slot_key_status |= 1 << SLOT_HH; - - if (r14 & 0x08) - new_slot_key_status |= 1 << SLOT_SD; - - if (r14 & 0x04) - new_slot_key_status |= 1 << SLOT_TOM; - - if (r14 & 0x02) - new_slot_key_status |= 1 << SLOT_CYM; - } - - updated_status = opll->slot_key_status ^ new_slot_key_status; - - if (updated_status) { - int i; - for (i = 0; i < 18; i++) - if (BIT(updated_status, i)) { - if (BIT(new_slot_key_status, i)) { - slotOn(opll, i); - } else { - slotOff(opll, i); - } - } - } - - opll->slot_key_status = new_slot_key_status; -} - -static INLINE void set_patch(OPLL *opll, int32_t ch, int32_t num) { - opll->patch_number[ch] = num; - MOD(opll, ch)->patch = &opll->patch[num * 2 + 0]; - CAR(opll, ch)->patch = &opll->patch[num * 2 + 1]; - request_update(MOD(opll, ch), UPDATE_ALL); - request_update(CAR(opll, ch), UPDATE_ALL); -} - -static INLINE void set_sus_flag(OPLL *opll, int ch, int flag) { - CAR(opll, ch)->sus_flag = flag; - request_update(CAR(opll, ch), UPDATE_EG); - if (MOD(opll, ch)->type & 1) { - MOD(opll, ch)->sus_flag = flag; - request_update(MOD(opll, ch), UPDATE_EG); - } -} - -/* set volume ( volume : 6bit, register value << 2 ) */ -static INLINE void set_volume(OPLL *opll, int ch, int volume) { - CAR(opll, ch)->volume = volume; - request_update(CAR(opll, ch), UPDATE_TLL); -} - -static INLINE void set_slot_volume(OPLL_SLOT *slot, int volume) { - slot->volume = volume; - request_update(slot, UPDATE_TLL); -} - -/* set f-Nnmber ( fnum : 9bit ) */ -static INLINE void set_fnumber(OPLL *opll, int ch, int fnum) { - OPLL_SLOT *car = CAR(opll, ch); - OPLL_SLOT *mod = MOD(opll, ch); - car->fnum = fnum; - car->blk_fnum = (car->blk_fnum & 0xe00) | (fnum & 0x1ff); - mod->fnum = fnum; - mod->blk_fnum = (mod->blk_fnum & 0xe00) | (fnum & 0x1ff); - request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); - request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); -} - -/* set block data (blk : 3bit ) */ -static INLINE void set_block(OPLL *opll, int ch, int blk) { - OPLL_SLOT *car = CAR(opll, ch); - OPLL_SLOT *mod = MOD(opll, ch); - car->blk = blk; - car->blk_fnum = ((blk & 7) << 9) | (car->blk_fnum & 0x1ff); - mod->blk = blk; - mod->blk_fnum = ((blk & 7) << 9) | (mod->blk_fnum & 0x1ff); - request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); - request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); -} - -static INLINE void update_rhythm_mode(OPLL *opll) { - const uint8_t new_rhythm_mode = (opll->reg[0x0e] >> 5) & 1; - - if (opll->rhythm_mode != new_rhythm_mode) { - - if (new_rhythm_mode) { - opll->slot[SLOT_HH].type = 3; - opll->slot[SLOT_HH].pg_keep = 1; - opll->slot[SLOT_SD].type = 3; - opll->slot[SLOT_TOM].type = 3; - opll->slot[SLOT_CYM].type = 3; - opll->slot[SLOT_CYM].pg_keep = 1; - set_patch(opll, 6, 16); - set_patch(opll, 7, 17); - set_patch(opll, 8, 18); - set_slot_volume(&opll->slot[SLOT_HH], ((opll->reg[0x37] >> 4) & 15) << 2); - set_slot_volume(&opll->slot[SLOT_TOM], ((opll->reg[0x38] >> 4) & 15) << 2); - } else { - opll->slot[SLOT_HH].type = 0; - opll->slot[SLOT_HH].pg_keep = 0; - opll->slot[SLOT_SD].type = 1; - opll->slot[SLOT_TOM].type = 0; - opll->slot[SLOT_CYM].type = 1; - opll->slot[SLOT_CYM].pg_keep = 0; - set_patch(opll, 6, opll->reg[0x36] >> 4); - set_patch(opll, 7, opll->reg[0x37] >> 4); - set_patch(opll, 8, opll->reg[0x38] >> 4); - } - } - - opll->rhythm_mode = new_rhythm_mode; -} - -static void update_ampm(OPLL *opll) { - if (opll->test_flag & 2) { - opll->pm_phase = 0; - opll->am_phase = 0; - } else { - opll->pm_phase += (opll->test_flag & 8) ? 1024 : 1; - opll->am_phase += (opll->test_flag & 8) ? 64 : 1; - } - opll->lfo_am = am_table[(opll->am_phase >> 6) % sizeof(am_table)]; -} - -static void update_noise(OPLL *opll, int cycle) { - int i; - for (i = 0; i < cycle; i++) { - if (opll->noise & 1) { - opll->noise ^= 0x800200; - } - opll->noise >>= 1; - } -} - -static void update_short_noise(OPLL *opll) { - const uint32_t pg_hh = opll->slot[SLOT_HH].pg_out; - const uint32_t pg_cym = opll->slot[SLOT_CYM].pg_out; - - const uint8_t h_bit2 = BIT(pg_hh, PG_BITS - 8); - const uint8_t h_bit7 = BIT(pg_hh, PG_BITS - 3); - const uint8_t h_bit3 = BIT(pg_hh, PG_BITS - 7); - - const uint8_t c_bit3 = BIT(pg_cym, PG_BITS - 7); - const uint8_t c_bit5 = BIT(pg_cym, PG_BITS - 5); - - opll->short_noise = (h_bit2 ^ h_bit7) | (h_bit3 ^ c_bit5) | (c_bit3 ^ c_bit5); -} - -static INLINE void calc_phase(OPLL_SLOT *slot, int32_t pm_phase, uint8_t reset) { - const int8_t pm = slot->patch->PM ? pm_table[(slot->fnum >> 6) & 7][(pm_phase >> 10) & 7] : 0; - if (reset) { - slot->pg_phase = 0; - } - slot->pg_phase += (((slot->fnum & 0x1ff) * 2 + pm) * ml_table[slot->patch->ML]) << slot->blk >> 2; - slot->pg_phase &= (DP_WIDTH - 1); - slot->pg_out = slot->pg_phase >> DP_BASE_BITS; -} - -static INLINE uint8_t lookup_attack_step(OPLL_SLOT *slot, uint32_t counter) { - int index; - - switch (slot->eg_rate_h) { - case 12: - index = (counter & 0xc) >> 1; - return 4 - eg_step_tables[slot->eg_rate_l][index]; - case 13: - index = (counter & 0xc) >> 1; - return 3 - eg_step_tables[slot->eg_rate_l][index]; - case 14: - index = (counter & 0xc) >> 1; - return 2 - eg_step_tables[slot->eg_rate_l][index]; - case 0: - case 15: - return 0; - default: - index = counter >> slot->eg_shift; - return eg_step_tables[slot->eg_rate_l][index & 7] ? 4 : 0; - } -} - -static INLINE uint8_t lookup_decay_step(OPLL_SLOT *slot, uint32_t counter) { - int index; - - switch (slot->eg_rate_h) { - case 0: - return 0; - case 13: - index = ((counter & 0xc) >> 1) | (counter & 1); - return eg_step_tables[slot->eg_rate_l][index]; - case 14: - index = ((counter & 0xc) >> 1); - return eg_step_tables[slot->eg_rate_l][index] + 1; - case 15: - return 2; - default: - index = counter >> slot->eg_shift; - return eg_step_tables[slot->eg_rate_l][index & 7]; - } -} - -static INLINE void start_envelope(OPLL_SLOT *slot) { - if (min(15, slot->patch->AR + (slot->rks >> 2)) == 15) { - slot->eg_state = DECAY; - slot->eg_out = 0; - } else { - slot->eg_state = ATTACK; - slot->eg_out = EG_MUTE; - } - request_update(slot, UPDATE_EG); -} - -static INLINE void calc_envelope(OPLL_SLOT *slot, OPLL_SLOT *buddy, uint16_t eg_counter, uint8_t test) { - - uint32_t mask = (1 << slot->eg_shift) - 1; - uint8_t s; - - if (slot->eg_state == ATTACK) { - if (0 < slot->eg_out && 0 < slot->eg_rate_h && (eg_counter & mask & ~3) == 0) { - s = lookup_attack_step(slot, eg_counter); - if (0 < s) { - slot->eg_out = max(0, ((int)slot->eg_out - (slot->eg_out >> s) - 1)); - } - } - } else { - if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) { - slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter)); - } - } - - switch (slot->eg_state) { - case DAMP: - if (slot->eg_out >= EG_MUTE) { - start_envelope(slot); - if (slot->type & 1) { - if (!slot->pg_keep) { - slot->pg_phase = 0; - } - if (buddy && !buddy->pg_keep) { - buddy->pg_phase = 0; - } - } - } - break; - - case ATTACK: - if (slot->eg_out == 0) { - slot->eg_state = DECAY; - request_update(slot, UPDATE_EG); - } - break; - - case DECAY: - if ((slot->eg_out >> 3) == slot->patch->SL) { - slot->eg_state = SUSTAIN; - request_update(slot, UPDATE_EG); - } - break; - - case SUSTAIN: - case RELEASE: - default: - break; - } - - if (test) { - slot->eg_out = 0; - } -} - -static void update_slots(OPLL *opll) { - int i; - opll->eg_counter++; - - for (i = 0; i < 18; i++) { - OPLL_SLOT *slot = &opll->slot[i]; - OPLL_SLOT *buddy = NULL; - if (slot->type == 0) { - buddy = &opll->slot[i + 1]; - } - if (slot->type == 1) { - buddy = &opll->slot[i - 1]; - } - if (slot->update_requests) { - commit_slot_update(slot); - } - calc_envelope(slot, buddy, opll->eg_counter, opll->test_flag & 1); - calc_phase(slot, opll->pm_phase, opll->test_flag & 4); - } -} - -/* output: -4095...4095 */ -static INLINE int16_t lookup_exp_table(uint16_t i) { - /* from andete's expressoin */ - int16_t t = (exp_table[(i & 0xff) ^ 0xff] + 1024); - int16_t res = t >> ((i & 0x7f00) >> 8); - return ((i & 0x8000) ? ~res : res) << 1; -} - -static INLINE int16_t to_linear(uint16_t h, OPLL_SLOT *slot, int16_t am) { - uint16_t att; - if (slot->eg_out >= EG_MAX) - return 0; - - att = min(EG_MAX, (slot->eg_out + slot->tll + am)) << 4; - return lookup_exp_table(h + att); -} - -static INLINE int16_t calc_slot_car(OPLL *opll, int ch, int16_t fm) { - OPLL_SLOT *slot = CAR(opll, ch); - - uint8_t am = slot->patch->AM ? opll->lfo_am : 0; - - slot->output[1] = slot->output[0]; - slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + 2 * (fm >> 1)) & (PG_WIDTH - 1)], slot, am); - - return slot->output[0]; -} - -static INLINE int16_t calc_slot_mod(OPLL *opll, int ch) { - OPLL_SLOT *slot = MOD(opll, ch); - - int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0; - uint8_t am = slot->patch->AM ? opll->lfo_am : 0; - - slot->output[1] = slot->output[0]; - slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + fm) & (PG_WIDTH - 1)], slot, am); - - return slot->output[0]; -} - -static INLINE int16_t calc_slot_tom(OPLL *opll) { - OPLL_SLOT *slot = MOD(opll, 8); - - return to_linear(slot->wave_table[slot->pg_out], slot, 0); -} - -/* Specify phase offset directly based on 10-bit (1024-length) sine table */ -#define _PD(phase) ((PG_BITS < 10) ? (phase >> (10 - PG_BITS)) : (phase << (PG_BITS - 10))) - -static INLINE int16_t calc_slot_snare(OPLL *opll) { - OPLL_SLOT *slot = CAR(opll, 7); - - uint32_t phase; - - if (BIT(slot->pg_out, PG_BITS - 2)) - phase = (opll->noise & 1) ? _PD(0x300) : _PD(0x200); - else - phase = (opll->noise & 1) ? _PD(0x0) : _PD(0x100); - - return to_linear(slot->wave_table[phase], slot, 0); -} - -static INLINE int16_t calc_slot_cym(OPLL *opll) { - OPLL_SLOT *slot = CAR(opll, 8); - - uint32_t phase = opll->short_noise ? _PD(0x300) : _PD(0x100); - - return to_linear(slot->wave_table[phase], slot, 0); -} - -static INLINE int16_t calc_slot_hat(OPLL *opll) { - OPLL_SLOT *slot = MOD(opll, 7); - - uint32_t phase; - - if (opll->short_noise) - phase = (opll->noise & 1) ? _PD(0x2d0) : _PD(0x234); - else - phase = (opll->noise & 1) ? _PD(0x34) : _PD(0xd0); - - return to_linear(slot->wave_table[phase], slot, 0); -} - -#define _MO(x) (-(x) >> 1) -#define _RO(x) (x) - -static void update_output(OPLL *opll) { - int16_t *out; - int i; - - update_ampm(opll); - update_short_noise(opll); - update_slots(opll); - - out = opll->ch_out; - - /* CH1-6 */ - for (i = 0; i < 6; i++) { - if (!(opll->mask & OPLL_MASK_CH(i))) { - out[i] = _MO(calc_slot_car(opll, i, calc_slot_mod(opll, i))); - } - } - - /* CH7 */ - if (!opll->rhythm_mode) { - if (!(opll->mask & OPLL_MASK_CH(6))) { - out[6] = _MO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); - } - } else { - if (!(opll->mask & OPLL_MASK_BD)) { - out[9] = _RO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); - } - } - update_noise(opll, 14); - - /* CH8 */ - if (!opll->rhythm_mode) { - if (!(opll->mask & OPLL_MASK_CH(7))) { - out[7] = _MO(calc_slot_car(opll, 7, calc_slot_mod(opll, 7))); - } - } else { - if (!(opll->mask & OPLL_MASK_HH)) { - out[10] = _RO(calc_slot_hat(opll)); - } - if (!(opll->mask & OPLL_MASK_SD)) { - out[11] = _RO(calc_slot_snare(opll)); - } - } - update_noise(opll, 2); - - /* CH9 */ - if (!opll->rhythm_mode) { - if (!(opll->mask & OPLL_MASK_CH(8))) { - out[8] = _MO(calc_slot_car(opll, 8, calc_slot_mod(opll, 8))); - } - } else { - if (!(opll->mask & OPLL_MASK_TOM)) { - out[12] = _RO(calc_slot_tom(opll)); - } - if (!(opll->mask & OPLL_MASK_CYM)) { - out[13] = _RO(calc_slot_cym(opll)); - } - } - update_noise(opll, 2); -} - -INLINE static void mix_output(OPLL *opll) { - int16_t out = 0; - int i; - for (i = 0; i < 14; i++) { - out += opll->ch_out[i]; - } - if (opll->conv) { - OPLL_RateConv_putData(opll->conv, 0, out); - } else { - opll->mix_out[0] = out; - } -} - -INLINE static void mix_output_stereo(OPLL *opll) { - int16_t *out = opll->mix_out; - int i; - out[0] = out[1] = 0; - for (i = 0; i < 14; i++) { - if (opll->pan[i] & 2) - out[0] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][0]); - if (opll->pan[i] & 1) - out[1] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][1]); - } - if (opll->conv) { - OPLL_RateConv_putData(opll->conv, 0, out[0]); - OPLL_RateConv_putData(opll->conv, 1, out[1]); - } -} - -/*********************************************************** - - External Interfaces - -***********************************************************/ - -OPLL *OPLL_new(uint32_t clk, uint32_t rate) { - OPLL *opll; - int i; - - if (!table_initialized) { - initializeTables(); - } - - opll = (OPLL *)calloc(sizeof(OPLL), 1); - if (opll == NULL) - return NULL; - - for (i = 0; i < 19 * 2; i++) - memcpy(&opll->patch[i], &null_patch, sizeof(OPLL_PATCH)); - - opll->clk = clk; - opll->rate = rate; - opll->mask = 0; - opll->conv = NULL; - opll->mix_out[0] = 0; - opll->mix_out[1] = 0; - - OPLL_reset(opll); - OPLL_setChipType(opll, 0); - OPLL_resetPatch(opll, 0); - return opll; -} - -void OPLL_delete(OPLL *opll) { - if (opll->conv) { - OPLL_RateConv_delete(opll->conv); - opll->conv = NULL; - } - free(opll); -} - -static void reset_rate_conversion_params(OPLL *opll) { - const double f_out = opll->rate; - const double f_inp = opll->clk / 72; - - opll->out_time = 0; - opll->out_step = ((uint32_t)f_inp) << 8; - opll->inp_step = ((uint32_t)f_out) << 8; - - if (opll->conv) { - OPLL_RateConv_delete(opll->conv); - opll->conv = NULL; - } - - if (floor(f_inp) != f_out && floor(f_inp + 0.5) != f_out) { - opll->conv = OPLL_RateConv_new(f_inp, f_out, 2); - } - - if (opll->conv) { - OPLL_RateConv_reset(opll->conv); - } -} - -void OPLL_reset(OPLL *opll) { - int i; - - if (!opll) - return; - - opll->adr = 0; - - opll->pm_phase = 0; - opll->am_phase = 0; - - opll->noise = 0x1; - opll->mask = 0; - - opll->rhythm_mode = 0; - opll->slot_key_status = 0; - opll->eg_counter = 0; - - reset_rate_conversion_params(opll); - - for (i = 0; i < 18; i++) - reset_slot(&opll->slot[i], i); - - for (i = 0; i < 9; i++) { - set_patch(opll, i, 0); - } - - for (i = 0; i < 0x40; i++) - OPLL_writeReg(opll, i, 0); - - for (i = 0; i < 15; i++) { - opll->pan[i] = 3; - opll->pan_fine[i][1] = opll->pan_fine[i][0] = 1.0f; - } - - for (i = 0; i < 14; i++) { - opll->ch_out[i] = 0; - } -} - -void OPLL_forceRefresh(OPLL *opll) { - int i; - - if (opll == NULL) - return; - - for (i = 0; i < 9; i++) { - set_patch(opll, i, opll->patch_number[i]); - } - - for (i = 0; i < 18; i++) { - request_update(&opll->slot[i], UPDATE_ALL); - } -} - -void OPLL_setRate(OPLL *opll, uint32_t rate) { - opll->rate = rate; - reset_rate_conversion_params(opll); -} - -void OPLL_setQuality(OPLL *opll, uint8_t q) {} - -void OPLL_setChipType(OPLL *opll, uint8_t type) { opll->chip_type = type; } - -void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t data) { - int ch, i; - - if (reg >= 0x40) - return; - - /* mirror registers */ - if ((0x19 <= reg && reg <= 0x1f) || (0x29 <= reg && reg <= 0x2f) || (0x39 <= reg && reg <= 0x3f)) { - reg -= 9; - } - - opll->reg[reg] = (uint8_t)data; - - switch (reg) { - case 0x00: - opll->patch[0].AM = (data >> 7) & 1; - opll->patch[0].PM = (data >> 6) & 1; - opll->patch[0].EG = (data >> 5) & 1; - opll->patch[0].KR = (data >> 4) & 1; - opll->patch[0].ML = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_RKS | UPDATE_EG); - } - } - break; - - case 0x01: - opll->patch[1].AM = (data >> 7) & 1; - opll->patch[1].PM = (data >> 6) & 1; - opll->patch[1].EG = (data >> 5) & 1; - opll->patch[1].KR = (data >> 4) & 1; - opll->patch[1].ML = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(CAR(opll, i), UPDATE_RKS | UPDATE_EG); - } - } - break; - - case 0x02: - opll->patch[0].KL = (data >> 6) & 3; - opll->patch[0].TL = (data)&63; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_TLL); - } - } - break; - - case 0x03: - opll->patch[1].KL = (data >> 6) & 3; - opll->patch[1].WS = (data >> 4) & 1; - opll->patch[0].WS = (data >> 3) & 1; - opll->patch[0].FB = (data)&7; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_WS); - request_update(CAR(opll, i), UPDATE_WS | UPDATE_TLL); - } - } - break; - - case 0x04: - opll->patch[0].AR = (data >> 4) & 15; - opll->patch[0].DR = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_EG); - } - } - break; - - case 0x05: - opll->patch[1].AR = (data >> 4) & 15; - opll->patch[1].DR = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(CAR(opll, i), UPDATE_EG); - } - } - break; - - case 0x06: - opll->patch[0].SL = (data >> 4) & 15; - opll->patch[0].RR = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_EG); - } - } - break; - - case 0x07: - opll->patch[1].SL = (data >> 4) & 15; - opll->patch[1].RR = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(CAR(opll, i), UPDATE_EG); - } - } - break; - - case 0x0e: - if (opll->chip_type == 1) - break; - update_rhythm_mode(opll); - update_key_status(opll); - break; - - case 0x0f: - opll->test_flag = data; - break; - - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - ch = reg - 0x10; - set_fnumber(opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8)); - break; - - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - ch = reg - 0x20; - set_fnumber(opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]); - set_block(opll, ch, (data >> 1) & 7); - set_sus_flag(opll, ch, (data >> 5) & 1); - update_key_status(opll); - break; - - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - if ((opll->reg[0x0e] & 32) && (reg >= 0x36)) { - switch (reg) { - case 0x37: - set_slot_volume(MOD(opll, 7), ((data >> 4) & 15) << 2); - break; - case 0x38: - set_slot_volume(MOD(opll, 8), ((data >> 4) & 15) << 2); - break; - default: - break; - } - } else { - set_patch(opll, reg - 0x30, (data >> 4) & 15); - } - set_volume(opll, reg - 0x30, (data & 15) << 2); - break; - - default: - break; - } -} - -void OPLL_writeIO(OPLL *opll, uint32_t adr, uint8_t val) { - if (adr & 1) - OPLL_writeReg(opll, opll->adr, val); - else - opll->adr = val; -} - -void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan) { opll->pan[ch & 15] = pan; } - -void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]) { - opll->pan_fine[ch & 15][0] = pan[0]; - opll->pan_fine[ch & 15][1] = pan[1]; -} - -void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch) { - patch[0].AM = (dump[0] >> 7) & 1; - patch[1].AM = (dump[1] >> 7) & 1; - patch[0].PM = (dump[0] >> 6) & 1; - patch[1].PM = (dump[1] >> 6) & 1; - patch[0].EG = (dump[0] >> 5) & 1; - patch[1].EG = (dump[1] >> 5) & 1; - patch[0].KR = (dump[0] >> 4) & 1; - patch[1].KR = (dump[1] >> 4) & 1; - patch[0].ML = (dump[0]) & 15; - patch[1].ML = (dump[1]) & 15; - patch[0].KL = (dump[2] >> 6) & 3; - patch[1].KL = (dump[3] >> 6) & 3; - patch[0].TL = (dump[2]) & 63; - patch[1].TL = 0; - patch[0].FB = (dump[3]) & 7; - patch[1].FB = 0; - patch[0].WS = (dump[3] >> 3) & 1; - patch[1].WS = (dump[3] >> 4) & 1; - patch[0].AR = (dump[4] >> 4) & 15; - patch[1].AR = (dump[5] >> 4) & 15; - patch[0].DR = (dump[4]) & 15; - patch[1].DR = (dump[5]) & 15; - patch[0].SL = (dump[6] >> 4) & 15; - patch[1].SL = (dump[7] >> 4) & 15; - patch[0].RR = (dump[6]) & 15; - patch[1].RR = (dump[7]) & 15; -} - -void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *patch) { - OPLL_dump2patch(default_inst[type] + num * 8, patch); -} - -void OPLL_setPatch(OPLL *opll, const uint8_t *dump) { - OPLL_PATCH patch[2]; - int i; - for (i = 0; i < 19; i++) { - OPLL_dump2patch(dump + i * 8, patch); - memcpy(&opll->patch[i * 2 + 0], &patch[0], sizeof(OPLL_PATCH)); - memcpy(&opll->patch[i * 2 + 1], &patch[1], sizeof(OPLL_PATCH)); - } -} - -void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump) { - dump[0] = (uint8_t)((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML); - dump[1] = (uint8_t)((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML); - dump[2] = (uint8_t)((patch[0].KL << 6) + patch[0].TL); - dump[3] = (uint8_t)((patch[1].KL << 6) + (patch[1].WS << 4) + (patch[0].WS << 3) + patch[0].FB); - dump[4] = (uint8_t)((patch[0].AR << 4) + patch[0].DR); - dump[5] = (uint8_t)((patch[1].AR << 4) + patch[1].DR); - dump[6] = (uint8_t)((patch[0].SL << 4) + patch[0].RR); - dump[7] = (uint8_t)((patch[1].SL << 4) + patch[1].RR); -} - -void OPLL_copyPatch(OPLL *opll, int32_t num, OPLL_PATCH *patch) { - memcpy(&opll->patch[num], patch, sizeof(OPLL_PATCH)); -} - -void OPLL_resetPatch(OPLL *opll, uint8_t type) { - int i; - for (i = 0; i < 19 * 2; i++) - OPLL_copyPatch(opll, i, &default_patch[type % OPLL_TONE_NUM][i]); -} - -int16_t OPLL_calc(OPLL *opll) { - while (opll->out_step > opll->out_time) { - opll->out_time += opll->inp_step; - update_output(opll); - mix_output(opll); - } - opll->out_time -= opll->out_step; - if (opll->conv) { - opll->mix_out[0] = OPLL_RateConv_getData(opll->conv, 0); - } - return opll->mix_out[0]; -} - -void OPLL_calcStereo(OPLL *opll, int32_t out[2]) { - while (opll->out_step > opll->out_time) { - opll->out_time += opll->inp_step; - update_output(opll); - mix_output_stereo(opll); - } - opll->out_time -= opll->out_step; - if (opll->conv) { - out[0] = OPLL_RateConv_getData(opll->conv, 0); - out[1] = OPLL_RateConv_getData(opll->conv, 1); - } else { - out[0] = opll->mix_out[0]; - out[1] = opll->mix_out[1]; - } -} - -uint32_t OPLL_setMask(OPLL *opll, uint32_t mask) { - uint32_t ret; - - if (opll) { - ret = opll->mask; - opll->mask = mask; - return ret; - } else - return 0; -} - -uint32_t OPLL_toggleMask(OPLL *opll, uint32_t mask) { - uint32_t ret; - - if (opll) { - ret = opll->mask; - opll->mask ^= mask; - return ret; - } else - return 0; -} diff --git a/extern/NSFplay/legacy/emu2413.h b/extern/NSFplay/legacy/emu2413.h deleted file mode 100644 index 2804f3fac..000000000 --- a/extern/NSFplay/legacy/emu2413.h +++ /dev/null @@ -1,247 +0,0 @@ -#ifndef _EMU2413_H_ -#define _EMU2413_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define OPLL_DEBUG 0 - -enum OPLL_TONE_ENUM { - OPLL_VRC7_TONE = 0, - OPLL_VRC7_RW_TONE, - OPLL_VRC7_FT36_TONE, - OPLL_VRC7_FT35_TONE, - OPLL_VRC7_MO_TONE, - OPLL_VRC7_KT2_TONE, - OPLL_VRC7_KT1_TONE, - OPLL_2413_TONE, - OPLL_281B_TONE -}; - -/* voice data */ -typedef struct __OPLL_PATCH { - uint32_t TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WS; -} OPLL_PATCH; - -/* slot */ -typedef struct __OPLL_SLOT { - uint8_t number; - - /* type flags: - * 000000SM - * |+-- M: 0:modulator 1:carrier - * +--- S: 0:normal 1:single slot mode (sd, tom, hh or cym) - */ - uint8_t type; - - OPLL_PATCH *patch; /* voice parameter */ - - /* slot output */ - int32_t output[2]; /* output value, latest and previous. */ - - /* phase generator (pg) */ - uint16_t *wave_table; /* wave table */ - uint32_t pg_phase; /* pg phase */ - uint32_t pg_out; /* pg output, as index of wave table */ - uint8_t pg_keep; /* if 1, pg_phase is preserved when key-on */ - uint16_t blk_fnum; /* (block << 9) | f-number */ - uint16_t fnum; /* f-number (9 bits) */ - uint8_t blk; /* block (3 bits) */ - - /* envelope generator (eg) */ - uint8_t eg_state; /* current state */ - int32_t volume; /* current volume */ - uint8_t key_flag; /* key-on flag 1:on 0:off */ - uint8_t sus_flag; /* key-sus option 1:on 0:off */ - uint16_t tll; /* total level + key scale level*/ - uint8_t rks; /* key scale offset (rks) for eg speed */ - uint8_t eg_rate_h; /* eg speed rate high 4bits */ - uint8_t eg_rate_l; /* eg speed rate low 2bits */ - uint32_t eg_shift; /* shift for eg global counter, controls envelope speed */ - uint32_t eg_out; /* eg output */ - - uint32_t update_requests; /* flags to debounce update */ - -#if OPLL_DEBUG - uint8_t last_eg_state; -#endif -} OPLL_SLOT; - -/* mask */ -#define OPLL_MASK_CH(x) (1 << (x)) -#define OPLL_MASK_HH (1 << (9)) -#define OPLL_MASK_CYM (1 << (10)) -#define OPLL_MASK_TOM (1 << (11)) -#define OPLL_MASK_SD (1 << (12)) -#define OPLL_MASK_BD (1 << (13)) -#define OPLL_MASK_RHYTHM (OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD) - -/* rate conveter */ -typedef struct __OPLL_RateConv { - int ch; - double timer; - double f_ratio; - int16_t *sinc_table; - int16_t **buf; -} OPLL_RateConv; - -OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch); -void OPLL_RateConv_reset(OPLL_RateConv *conv); -void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data); -int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch); -void OPLL_RateConv_delete(OPLL_RateConv *conv); - -typedef struct __OPLL { - uint32_t clk; - uint32_t rate; - - uint8_t chip_type; - - uint32_t adr; - - uint32_t inp_step; - uint32_t out_step; - uint32_t out_time; - - uint8_t reg[0x40]; - uint8_t test_flag; - uint32_t slot_key_status; - uint8_t rhythm_mode; - - uint32_t eg_counter; - - uint32_t pm_phase; - int32_t am_phase; - - uint8_t lfo_am; - - uint32_t noise; - uint8_t short_noise; - - int32_t patch_number[9]; - OPLL_SLOT slot[18]; - OPLL_PATCH patch[19 * 2]; - - uint8_t pan[16]; - float pan_fine[16][2]; - - uint32_t mask; - - /* channel output */ - /* 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym */ - int16_t ch_out[14]; - - int16_t mix_out[2]; - - OPLL_RateConv *conv; -} OPLL; - -OPLL *OPLL_new(uint32_t clk, uint32_t rate); -void OPLL_delete(OPLL *); - -void OPLL_reset(OPLL *); -void OPLL_resetPatch(OPLL *, uint8_t); - -/** - * Set output wave sampling rate. - * @param rate sampling rate. If clock / 72 (typically 49716 or 49715 at 3.58MHz) is set, the internal rate converter is - * disabled. - */ -void OPLL_setRate(OPLL *opll, uint32_t rate); - -/** - * Set internal calcuration quality. Currently no effects, just for compatibility. - * >= v1.0.0 always synthesizes internal output at clock/72 Hz. - */ -void OPLL_setQuality(OPLL *opll, uint8_t q); - -/** - * Set pan pot (extra function - not YM2413 chip feature) - * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved - * @param pan 0:mute 1:right 2:left 3:center - * ``` - * pan: 76543210 - * |+- bit 1: enable Left output - * +-- bit 0: enable Right output - * ``` - */ -void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan); - -/** - * Set fine-grained panning - * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved - * @param pan output strength of left/right channel. - * pan[0]: left, pan[1]: right. pan[0]=pan[1]=1.0f for center. - */ -void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]); - -/** - * Set chip type. If vrc7 is selected, r#14 is ignored. - * This method not change the current ROM patch set. - * To change ROM patch set, use OPLL_resetPatch. - * @param type 0:YM2413 1:VRC7 - */ -void OPLL_setChipType(OPLL *opll, uint8_t type); - -void OPLL_writeIO(OPLL *opll, uint32_t reg, uint8_t val); -void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t val); - -/** - * Calculate one sample - */ -int16_t OPLL_calc(OPLL *opll); - -/** - * Calulate stereo sample - */ -void OPLL_calcStereo(OPLL *opll, int32_t out[2]); - -void OPLL_setPatch(OPLL *, const uint8_t *dump); -void OPLL_copyPatch(OPLL *, int32_t, OPLL_PATCH *); - -/** - * Force to refresh. - * External program should call this function after updating patch parameters. - */ -void OPLL_forceRefresh(OPLL *); - -void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch); -void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump); -void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *); - -/** - * Set channel mask - * @param mask mask flag: OPLL_MASK_* can be used. - * - bit 0..8: mask for ch 1 to 9 (OPLL_MASK_CH(i)) - * - bit 9: mask for Hi-Hat (OPLL_MASK_HH) - * - bit 10: mask for Top-Cym (OPLL_MASK_CYM) - * - bit 11: mask for Tom (OPLL_MASK_TOM) - * - bit 12: mask for Snare Drum (OPLL_MASK_SD) - * - bit 13: mask for Bass Drum (OPLL_MASK_BD) - */ -uint32_t OPLL_setMask(OPLL *, uint32_t mask); - -/** - * Toggler channel mask flag - */ -uint32_t OPLL_toggleMask(OPLL *, uint32_t mask); - -/* for compatibility */ -#define OPLL_set_rate OPLL_setRate -#define OPLL_set_quality OPLL_setQuality -#define OPLL_set_pan OPLL_setPan -#define OPLL_set_pan_fine OPLL_setPanFine -#define OPLL_calc_stereo OPLL_calcStereo -#define OPLL_reset_patch OPLL_resetPatch -#define OPLL_dump2patch OPLL_dumpToPatch -#define OPLL_patch2dump OPLL_patchToDump -#define OPLL_setChipMode OPLL_setChipType - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/NSFplay/legacy/emutypes.h b/extern/NSFplay/legacy/emutypes.h deleted file mode 100644 index bf5d7e1bf..000000000 --- a/extern/NSFplay/legacy/emutypes.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _EMUTYPES_H_ -#define _EMUTYPES_H_ - -#if defined(_MSC_VER) -#define INLINE __forceinline -#elif defined(__GNUC__) -#define INLINE __inline__ -#elif defined(_MWERKS_) -#define INLINE inline -#else -#define INLINE -#endif - -#if defined(EMU_DLL_IMPORTS) -#define EMU2149_DLL_IMPORTS -#define EMU2212_DLL_IMPORTS -#define EMU2413_DLL_IMPORTS -#define EMU8950_DLL_IMPORTS -#define EMU76489_DLL_IMPORTS -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned int e_uint; -typedef signed int e_int; - -typedef unsigned char e_uint8 ; -typedef signed char e_int8 ; - -typedef unsigned short e_uint16 ; -typedef signed short e_int16 ; - -typedef unsigned int e_uint32 ; -typedef signed int e_int32 ; - -#ifdef __cplusplus -} -#endif -#endif diff --git a/extern/NSFplay/legacy/vrc7tone_ft35.h b/extern/NSFplay/legacy/vrc7tone_ft35.h deleted file mode 100644 index 7af08bcf7..000000000 --- a/extern/NSFplay/legacy/vrc7tone_ft35.h +++ /dev/null @@ -1,20 +0,0 @@ -// patch set by Mitsutaka Okazaki used in FamiTracker 0.3.5 and prior (6/24/2001) -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x33, 0x01, 0x09, 0x0e, 0x94, 0x90, 0x40, 0x01, -0x13, 0x41, 0x0f, 0x0d, 0xce, 0xd3, 0x43, 0x13, -0x01, 0x12, 0x1b, 0x06, 0xff, 0xd2, 0x00, 0x32, -0x61, 0x61, 0x1b, 0x07, 0xaf, 0x63, 0x20, 0x28, -0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x08, 0x28, -0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xf8, -0x21, 0x61, 0x1c, 0x07, 0x82, 0x81, 0x10, 0x17, -0x23, 0x21, 0x20, 0x1f, 0xc0, 0x71, 0x07, 0x47, -0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xf8, -0x17, 0x21, 0x28, 0x07, 0xff, 0x83, 0x02, 0xf8, -0x97, 0x81, 0x25, 0x07, 0xcf, 0xc8, 0x02, 0x14, -0x21, 0x21, 0x54, 0x0f, 0x80, 0x7f, 0x07, 0x07, -0x01, 0x01, 0x56, 0x03, 0xd3, 0xb2, 0x43, 0x58, -0x31, 0x21, 0x0c, 0x03, 0x82, 0xc0, 0x40, 0x07, -0x21, 0x01, 0x0c, 0x03, 0xd4, 0xd3, 0x40, 0x84, -0x04, 0x21, 0x28, 0x00, 0xdf, 0xf8, 0xff, 0xf8, -0x23, 0x22, 0x00, 0x00, 0xa8, 0xf8, 0xf8, 0xf8, -0x25, 0x18, 0x00, 0x00, 0xf8, 0xa9, 0xf8, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_ft36.h b/extern/NSFplay/legacy/vrc7tone_ft36.h deleted file mode 100644 index f693955e6..000000000 --- a/extern/NSFplay/legacy/vrc7tone_ft36.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set by quietust (1/18/2004), used in FamiTracker 0.3.6 -// Source: http://nesdev.com/cgi-bin/wwwthreads/showpost.pl?Board=NESemdev&Number=1440 -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17, -0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12, -0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02, -0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27, -0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, -0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95, -0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07, -0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15, -0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11, -0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12, -0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4, -0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16, -0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51, -0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05, -0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt1.h b/extern/NSFplay/legacy/vrc7tone_kt1.h deleted file mode 100644 index 59cff8a3a..000000000 --- a/extern/NSFplay/legacy/vrc7tone_kt1.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set 1 by kevtris (11/14/1999) -// http://kevtris.org/nes/vrcvii.txt -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x05, 0x03, 0x10, 0x06, 0x74, 0xA1, 0x13, 0xF4, -0x05, 0x01, 0x16, 0x00, 0xF9, 0xA2, 0x15, 0xF5, -0x01, 0x41, 0x11, 0x00, 0xA0, 0xA0, 0x83, 0x95, -0x01, 0x41, 0x17, 0x00, 0x60, 0xF0, 0x83, 0x95, -0x24, 0x41, 0x1F, 0x00, 0x50, 0xB0, 0x94, 0x94, -0x05, 0x01, 0x0B, 0x04, 0x65, 0xA0, 0x54, 0x95, -0x11, 0x41, 0x0E, 0x04, 0x70, 0xC7, 0x13, 0x10, -0x02, 0x44, 0x16, 0x06, 0xE0, 0xE0, 0x31, 0x35, -0x48, 0x22, 0x22, 0x07, 0x50, 0xA1, 0xA5, 0xF4, -0x05, 0xA1, 0x18, 0x00, 0xA2, 0xA2, 0xF5, 0xF5, -0x07, 0x81, 0x2B, 0x05, 0xA5, 0xA5, 0x03, 0x03, -0x01, 0x41, 0x08, 0x08, 0xA0, 0xA0, 0x83, 0x95, -0x21, 0x61, 0x12, 0x00, 0x93, 0x92, 0x74, 0x75, -0x21, 0x62, 0x21, 0x00, 0x84, 0x85, 0x34, 0x15, -0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x34, 0x15, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt2.h b/extern/NSFplay/legacy/vrc7tone_kt2.h deleted file mode 100644 index 92caa42bf..000000000 --- a/extern/NSFplay/legacy/vrc7tone_kt2.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set 2 by kevtris (11/15/1999) -// http://kevtris.org/nes/vrcvii.txt -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x31, 0x22, 0x23, 0x07, 0xF0, 0xF0, 0xE8, 0xF7, -0x03, 0x31, 0x68, 0x05, 0xF2, 0x74, 0x79, 0x9C, -0x01, 0x51, 0x72, 0x04, 0xF1, 0xD3, 0x9D, 0x8B, -0x22, 0x61, 0x1B, 0x05, 0xC0, 0xA1, 0xF8, 0xE8, -0x22, 0x61, 0x2C, 0x03, 0xD2, 0xA1, 0xA7, 0xE8, -0x31, 0x22, 0xFA, 0x01, 0xF1, 0xF1, 0xF4, 0xEE, -0x21, 0x61, 0x28, 0x06, 0xF1, 0xF1, 0xCE, 0x9B, -0x27, 0x61, 0x60, 0x00, 0xF0, 0xF0, 0xFF, 0xFD, -0x60, 0x21, 0x2B, 0x06, 0x85, 0xF1, 0x79, 0x9D, -0x31, 0xA1, 0xFF, 0x0A, 0x53, 0x62, 0x5E, 0xAF, -0x03, 0xA1, 0x70, 0x0F, 0xD4, 0xA3, 0x94, 0xBE, -0x2B, 0x61, 0xE4, 0x07, 0xF6, 0x93, 0xBD, 0xAC, -0x21, 0x63, 0xED, 0x07, 0x77, 0xF1, 0xC7, 0xE8, -0x21, 0x61, 0x2A, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, -0x21, 0x63, 0x37, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_mo.h b/extern/NSFplay/legacy/vrc7tone_mo.h deleted file mode 100644 index 3b216f874..000000000 --- a/extern/NSFplay/legacy/vrc7tone_mo.h +++ /dev/null @@ -1,20 +0,0 @@ -/* VRC7 TONES by okazaki@angel.ne.jp (4/10/2004) */ -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x33,0x01,0x09,0x0e,0x94,0x90,0x40,0x01, -0x13,0x41,0x0f,0x0d,0xce,0xd3,0x43,0x13, -0x01,0x12,0x1b,0x06,0xff,0xd2,0x00,0x32, -0x61,0x61,0x1b,0x07,0xaf,0x63,0x20,0x28, -0x22,0x21,0x1e,0x06,0xf0,0x76,0x08,0x28, -0x66,0x21,0x15,0x00,0x93,0x94,0x20,0xf8, -0x21,0x61,0x1c,0x07,0x82,0x81,0x10,0x17, -0x23,0x21,0x20,0x1f,0xc0,0x71,0x07,0x47, -0x25,0x31,0x26,0x05,0x64,0x41,0x18,0xf8, -0x17,0x21,0x28,0x07,0xff,0x83,0x02,0xf8, -0x97,0x81,0x25,0x07,0xcf,0xc8,0x02,0x14, -0x21,0x21,0x54,0x0f,0x80,0x7f,0x07,0x07, -0x01,0x01,0x56,0x03,0xd3,0xb2,0x43,0x58, -0x31,0x21,0x0c,0x03,0x82,0xc0,0x40,0x07, -0x21,0x01,0x0c,0x03,0xd4,0xd3,0x40,0x84, -0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, -0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, -0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_nuke.h b/extern/NSFplay/legacy/vrc7tone_nuke.h deleted file mode 100644 index 0afcc4ce0..000000000 --- a/extern/NSFplay/legacy/vrc7tone_nuke.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set by Nuke.YKT (3/15/2019) -// https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#ym2413_instruments -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27, -0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12, -0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12, -0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27, -0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28, -0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4, -0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07, -0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17, -0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01, -0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02, -0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12, -0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16, -0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02, -0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6, -0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06, -0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, -0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x68, -0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_rw.h b/extern/NSFplay/legacy/vrc7tone_rw.h deleted file mode 100644 index bf2261208..000000000 --- a/extern/NSFplay/legacy/vrc7tone_rw.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set by rainwarrior (8/01/2012) -// http://forums.nesdev.com/viewtopic.php?f=6&t=9141 -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x21, 0x05, 0x06, 0xB8, 0x82, 0x42, 0x27, -0x13, 0x41, 0x13, 0x0D, 0xD8, 0xD6, 0x23, 0x12, -0x31, 0x11, 0x08, 0x08, 0xFA, 0x9A, 0x22, 0x02, -0x31, 0x61, 0x18, 0x07, 0x78, 0x64, 0x30, 0x27, -0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, -0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0xF5, -0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x16, 0x07, -0x23, 0x21, 0x1A, 0x17, 0xCF, 0x72, 0x25, 0x17, -0x15, 0x11, 0x25, 0x00, 0x4F, 0x71, 0x00, 0x11, -0x85, 0x01, 0x12, 0x0F, 0x99, 0xA2, 0x40, 0x02, -0x07, 0xC1, 0x69, 0x07, 0xF3, 0xF5, 0xA7, 0x12, -0x71, 0x23, 0x0D, 0x06, 0x66, 0x75, 0x23, 0x16, -0x01, 0x02, 0xD3, 0x05, 0xA3, 0x92, 0xF7, 0x52, -0x61, 0x63, 0x0C, 0x00, 0x94, 0xAF, 0x34, 0x06, -0x21, 0x62, 0x0D, 0x00, 0xB1, 0xA0, 0x54, 0x17, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/nes_apu.cpp b/extern/NSFplay/nes_apu.cpp index 9d50dfc78..b2ebb677a 100644 --- a/extern/NSFplay/nes_apu.cpp +++ b/extern/NSFplay/nes_apu.cpp @@ -1,400 +1,400 @@ -// -// NES 2A03 -// -#include -#include "nes_apu.h" - -namespace xgm -{ - void NES_APU::sweep_sqr (int i) - { - int shifted = freq[i] >> sweep_amount[i]; - if (i == 0 && sweep_mode[i]) shifted += 1; - sfreq[i] = freq[i] + (sweep_mode[i] ? -shifted : shifted); - //DEBUG_OUT("shifted[%d] = %d (%d >> %d)\n",i,shifted,freq[i],sweep_amount[i]); - } - - void NES_APU::FrameSequence(int s) - { - //DEBUG_OUT("FrameSequence(%d)\n",s); - - if (s > 3) return; // no operation in step 4 - - // 240hz clock - for (int i=0; i < 2; ++i) - { - bool divider = false; - if (envelope_write[i]) - { - envelope_write[i] = false; - envelope_counter[i] = 15; - envelope_div[i] = 0; - } - else - { - ++envelope_div[i]; - if (envelope_div[i] > envelope_div_period[i]) - { - divider = true; - envelope_div[i] = 0; - } - } - if (divider) - { - if (envelope_loop[i] && envelope_counter[i] == 0) - envelope_counter[i] = 15; - else if (envelope_counter[i] > 0) - --envelope_counter[i]; - } - } - - // 120hz clock - if ((s&1) == 0) - for (int i=0; i < 2; ++i) - { - if (!envelope_loop[i] && (length_counter[i] > 0)) - --length_counter[i]; - - if (sweep_enable[i]) - { - //DEBUG_OUT("Clock sweep: %d\n", i); - - --sweep_div[i]; - if (sweep_div[i] <= 0) - { - sweep_sqr(i); // calculate new sweep target - - //DEBUG_OUT("sweep_div[%d] (0/%d)\n",i,sweep_div_period[i]); - //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%d\n",i,freq[i],i,sfreq[i]); - - if (freq[i] >= 8 && sfreq[i] < 0x800 && sweep_amount[i] > 0) // update frequency if appropriate - { - freq[i] = sfreq[i] < 0 ? 0 : sfreq[i]; - } - sweep_div[i] = sweep_div_period[i] + 1; - - //DEBUG_OUT("freq[%d]=%d\n",i,freq[i]); - } - - if (sweep_write[i]) - { - sweep_div[i] = sweep_div_period[i] + 1; - sweep_write[i] = false; - } - } - } - - } - - INT32 NES_APU::calc_sqr (int i, UINT32 clocks) - { - static const INT16 sqrtbl[4][16] = { - {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} - }; - - scounter[i] -= clocks; - while (scounter[i] < 0) - { - sphase[i] = (sphase[i] + 1) & 15; - scounter[i] += freq[i] + 1; - } - - INT32 ret = 0; - if (length_counter[i] > 0 && - freq[i] >= 8 && - sfreq[i] < 0x800 - ) - { - int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; - ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; - } - - return ret; - } - - bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) - { - if (0x4000 <= adr && adr < 0x4008) - { - val |= reg[adr&0x7]; - return true; - } - else if(adr==0x4015) - { - val |= (length_counter[1]?2:0)|(length_counter[0]?1:0); - return true; - } - else - return false; - } - - void NES_APU::Tick (UINT32 clocks) - { - out[0] = calc_sqr(0, clocks); - out[1] = calc_sqr(1, clocks); - } - - // ¶¬‚³‚ê‚é”gŒ`‚ÌU•‚Í0-8191 - UINT32 NES_APU::Render (INT32 b[2]) - { - out[0] = (mask & 1) ? 0 : out[0]; - out[1] = (mask & 2) ? 0 : out[1]; - - INT32 m[2]; - - if(option[OPT_NONLINEAR_MIXER]) - { - INT32 voltage = square_table[out[0] + out[1]]; - m[0] = out[0] << 6; - m[1] = out[1] << 6; - INT32 ref = m[0] + m[1]; - if (ref > 0) - { - m[0] = (m[0] * voltage) / ref; - m[1] = (m[1] * voltage) / ref; - } - else - { - m[0] = voltage; - m[1] = voltage; - } - } - else - { - m[0] = (out[0] * square_linear) / 15; - m[1] = (out[1] * square_linear) / 15; - } - - b[0] = m[0] * sm[0][0]; - b[0] += m[1] * sm[0][1]; - b[0] >>= 7; - - b[1] = m[0] * sm[1][0]; - b[1] += m[1] * sm[1][1]; - b[1] >>= 7; - - return 2; - } - - NES_APU::NES_APU () - { - SetClock (DEFAULT_CLOCK); - SetRate (DEFAULT_RATE); - option[OPT_UNMUTE_ON_RESET] = true; - option[OPT_PHASE_REFRESH] = true; - option[OPT_NONLINEAR_MIXER] = true; - option[OPT_DUTY_SWAP] = false; - option[OPT_NEGATE_SWEEP_INIT] = false; - - square_table[0] = 0; - for(int i=1;i<32;i++) - square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); - - square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear - - for(int c=0;c<2;++c) - for(int t=0;t<2;++t) - sm[c][t] = 128; - } - - NES_APU::~NES_APU () - { - } - - void NES_APU::Reset () - { - int i; - gclock = 0; - mask = 0; - - for (int i=0; i<2; ++i) - { - scounter[i] = 0; - sphase[i] = 0; - duty[i] = 0; - volume[i] = 0; - freq[i] = 0; - sfreq[i] = 0; - sweep_enable[i] = 0; - sweep_mode[i] = 0; - sweep_write[i] = 0; - sweep_div_period[i] = 0; - sweep_div[i] = 1; - sweep_amount[i] = 0; - envelope_disable[i] = 0; - envelope_loop[i] = 0; - envelope_write[i] = 0; - envelope_div_period[i] = 0; - envelope_div[0] = 0; - envelope_counter[i] = 0; - length_counter[i] = 0; - enable[i] = 0; - } - - for (i = 0x4000; i < 0x4008; i++) - Write (i, 0); - - Write (0x4015, 0); - if (option[OPT_UNMUTE_ON_RESET]) - Write (0x4015, 0x0f); - if (option[OPT_NEGATE_SWEEP_INIT]) - { - Write (0x4001, 0x08); - Write (0x4005, 0x08); - } - - for (i = 0; i < 2; i++) - out[i] = 0; - - SetRate(rate); - } - - void NES_APU::SetOption (int id, int val) - { - if(id 1) return; - sm[0][trk] = mixl; - sm[1][trk] = mixr; - } - - ITrackInfo *NES_APU::GetTrackInfo(int trk) - { - trkinfo[trk]._freq = freq[trk]; - if(freq[trk]) - trkinfo[trk].freq = clock/16/(freq[trk] + 1); - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = out[trk]; - trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); - trkinfo[trk].key = - enable[trk] && - length_counter[trk] > 0 && - freq[trk] >= 8 && - sfreq[trk] < 0x800 && - (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); - trkinfo[trk].tone = duty[trk]; - trkinfo[trk].max_volume = 15; - return &trkinfo[trk]; - } - - bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) - { - int ch; - - static const UINT8 length_table[32] = { - 0x0A, 0xFE, - 0x14, 0x02, - 0x28, 0x04, - 0x50, 0x06, - 0xA0, 0x08, - 0x3C, 0x0A, - 0x0E, 0x0C, - 0x1A, 0x0E, - 0x0C, 0x10, - 0x18, 0x12, - 0x30, 0x14, - 0x60, 0x16, - 0xC0, 0x18, - 0x48, 0x1A, - 0x10, 0x1C, - 0x20, 0x1E - }; - - if (0x4000 <= adr && adr < 0x4008) - { - //DEBUG_OUT("$%04X = %02X\n",adr,val); - - adr &= 0xf; - ch = adr >> 2; - switch (adr) - { - case 0x0: - case 0x4: - volume[ch] = val & 15; - envelope_disable[ch] = (val >> 4) & 1; - envelope_loop[ch] = (val >> 5) & 1; - envelope_div_period[ch] = (val & 15); - duty[ch] = (val >> 6) & 3; - if (option[OPT_DUTY_SWAP]) - { - if (duty[ch] == 1) duty[ch] = 2; - else if (duty[ch] == 2) duty[ch] = 1; - } - break; - - case 0x1: - case 0x5: - sweep_enable[ch] = (val >> 7) & 1; - sweep_div_period[ch] = (((val >> 4) & 7)); - sweep_mode[ch] = (val >> 3) & 1; - sweep_amount[ch] = val & 7; - sweep_write[ch] = true; - sweep_sqr(ch); - break; - - case 0x2: - case 0x6: - freq[ch] = val | (freq[ch] & 0x700) ; - sweep_sqr(ch); - break; - - case 0x3: - case 0x7: - freq[ch] = (freq[ch] & 0xFF) | ((val & 0x7) << 8) ; - if (option[OPT_PHASE_REFRESH]) - sphase[ch] = 0; - envelope_write[ch] = true; - if (enable[ch]) - { - length_counter[ch] = length_table[(val >> 3) & 0x1f]; - } - sweep_sqr(ch); - break; - - default: - return false; - } - reg[adr] = val; - return true; - } - else if (adr == 0x4015) - { - enable[0] = (val & 1) ? true : false; - enable[1] = (val & 2) ? true : false; - - if (!enable[0]) - length_counter[0] = 0; - if (!enable[1]) - length_counter[1] = 0; - - reg[adr-0x4000] = val; - return true; - } - - // 4017 is handled in nes_dmc.cpp - //else if (adr == 0x4017) - //{ - //} - - return false; - } -} // namespace xgm; +// +// NES 2A03 +// +#include +#include "nes_apu.h" + +namespace xgm +{ + void NES_APU::sweep_sqr (int i) + { + int shifted = freq[i] >> sweep_amount[i]; + if (i == 0 && sweep_mode[i]) shifted += 1; + sfreq[i] = freq[i] + (sweep_mode[i] ? -shifted : shifted); + //DEBUG_OUT("shifted[%d] = %d (%d >> %d)Â¥n",i,shifted,freq[i],sweep_amount[i]); + } + + void NES_APU::FrameSequence(int s) + { + //DEBUG_OUT("FrameSequence(%d)Â¥n",s); + + if (s > 3) return; // no operation in step 4 + + // 240hz clock + for (int i=0; i < 2; ++i) + { + bool divider = false; + if (envelope_write[i]) + { + envelope_write[i] = false; + envelope_counter[i] = 15; + envelope_div[i] = 0; + } + else + { + ++envelope_div[i]; + if (envelope_div[i] > envelope_div_period[i]) + { + divider = true; + envelope_div[i] = 0; + } + } + if (divider) + { + if (envelope_loop[i] && envelope_counter[i] == 0) + envelope_counter[i] = 15; + else if (envelope_counter[i] > 0) + --envelope_counter[i]; + } + } + + // 120hz clock + if ((s&1) == 0) + for (int i=0; i < 2; ++i) + { + if (!envelope_loop[i] && (length_counter[i] > 0)) + --length_counter[i]; + + if (sweep_enable[i]) + { + //DEBUG_OUT("Clock sweep: %dÂ¥n", i); + + --sweep_div[i]; + if (sweep_div[i] <= 0) + { + sweep_sqr(i); // calculate new sweep target + + //DEBUG_OUT("sweep_div[%d] (0/%d)Â¥n",i,sweep_div_period[i]); + //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%dÂ¥n",i,freq[i],i,sfreq[i]); + + if (freq[i] >= 8 && sfreq[i] < 0x800 && sweep_amount[i] > 0) // update frequency if appropriate + { + freq[i] = sfreq[i] < 0 ? 0 : sfreq[i]; + } + sweep_div[i] = sweep_div_period[i] + 1; + + //DEBUG_OUT("freq[%d]=%dÂ¥n",i,freq[i]); + } + + if (sweep_write[i]) + { + sweep_div[i] = sweep_div_period[i] + 1; + sweep_write[i] = false; + } + } + } + + } + + INT32 NES_APU::calc_sqr (int i, UINT32 clocks) + { + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + scounter[i] -= clocks; + while (scounter[i] < 0) + { + sphase[i] = (sphase[i] + 1) & 15; + scounter[i] += freq[i] + 1; + } + + INT32 ret = 0; + if (length_counter[i] > 0 && + freq[i] >= 8 && + sfreq[i] < 0x800 + ) + { + int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; + ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; + } + + return ret; + } + + bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + if (0x4000 <= adr && adr < 0x4008) + { + val |= reg[adr&0x7]; + return true; + } + else if(adr==0x4015) + { + val |= (length_counter[1]?2:0)|(length_counter[0]?1:0); + return true; + } + else + return false; + } + + void NES_APU::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0, clocks); + out[1] = calc_sqr(1, clocks); + } + + // ツé’カツé’ャツã¤ï½³ï¾‚ã¥ï½ªï¾‚ã¥ï½©ï¾‚波ツ形ツã¥å€‹æŒ¯ï¾‚閉敖ã¥0-8191 + UINT32 NES_APU::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + + INT32 m[2]; + + if(option[OPT_NONLINEAR_MIXER]) + { + INT32 voltage = square_table[out[0] + out[1]]; + m[0] = out[0] << 6; + m[1] = out[1] << 6; + INT32 ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + } + else + { + m[0] = (out[0] * square_linear) / 15; + m[1] = (out[1] * square_linear) / 15; + } + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] >>= 7; + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] >>= 7; + + return 2; + } + + NES_APU::NES_APU () + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + option[OPT_UNMUTE_ON_RESET] = true; + option[OPT_PHASE_REFRESH] = true; + option[OPT_NONLINEAR_MIXER] = true; + option[OPT_DUTY_SWAP] = false; + option[OPT_NEGATE_SWEEP_INIT] = false; + + square_table[0] = 0; + for(int i=1;i<32;i++) + square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear + + for(int c=0;c<2;++c) + for(int t=0;t<2;++t) + sm[c][t] = 128; + } + + NES_APU::‾NES_APU () + { + } + + void NES_APU::Reset () + { + int i; + gclock = 0; + mask = 0; + + for (int i=0; i<2; ++i) + { + scounter[i] = 0; + sphase[i] = 0; + duty[i] = 0; + volume[i] = 0; + freq[i] = 0; + sfreq[i] = 0; + sweep_enable[i] = 0; + sweep_mode[i] = 0; + sweep_write[i] = 0; + sweep_div_period[i] = 0; + sweep_div[i] = 1; + sweep_amount[i] = 0; + envelope_disable[i] = 0; + envelope_loop[i] = 0; + envelope_write[i] = 0; + envelope_div_period[i] = 0; + envelope_div[0] = 0; + envelope_counter[i] = 0; + length_counter[i] = 0; + enable[i] = 0; + } + + for (i = 0x4000; i < 0x4008; i++) + Write (i, 0); + + Write (0x4015, 0); + if (option[OPT_UNMUTE_ON_RESET]) + Write (0x4015, 0x0f); + if (option[OPT_NEGATE_SWEEP_INIT]) + { + Write (0x4001, 0x08); + Write (0x4005, 0x08); + } + + for (i = 0; i < 2; i++) + out[i] = 0; + + SetRate(rate); + } + + void NES_APU::SetOption (int id, int val) + { + if(id 1) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_APU::GetTrackInfo(int trk) + { + trkinfo[trk]._freq = freq[trk]; + if(freq[trk]) + trkinfo[trk].freq = clock/16/(freq[trk] + 1); + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = out[trk]; + trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); + trkinfo[trk].key = + enable[trk] && + length_counter[trk] > 0 && + freq[trk] >= 8 && + sfreq[trk] < 0x800 && + (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); + trkinfo[trk].tone = duty[trk]; + trkinfo[trk].max_volume = 15; + return &trkinfo[trk]; + } + + bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if (0x4000 <= adr && adr < 0x4008) + { + //DEBUG_OUT("$%04X = %02XÂ¥n",adr,val); + + adr &= 0xf; + ch = adr >> 2; + switch (adr) + { + case 0x0: + case 0x4: + volume[ch] = val & 15; + envelope_disable[ch] = (val >> 4) & 1; + envelope_loop[ch] = (val >> 5) & 1; + envelope_div_period[ch] = (val & 15); + duty[ch] = (val >> 6) & 3; + if (option[OPT_DUTY_SWAP]) + { + if (duty[ch] == 1) duty[ch] = 2; + else if (duty[ch] == 2) duty[ch] = 1; + } + break; + + case 0x1: + case 0x5: + sweep_enable[ch] = (val >> 7) & 1; + sweep_div_period[ch] = (((val >> 4) & 7)); + sweep_mode[ch] = (val >> 3) & 1; + sweep_amount[ch] = val & 7; + sweep_write[ch] = true; + sweep_sqr(ch); + break; + + case 0x2: + case 0x6: + freq[ch] = val | (freq[ch] & 0x700) ; + sweep_sqr(ch); + break; + + case 0x3: + case 0x7: + freq[ch] = (freq[ch] & 0xFF) | ((val & 0x7) << 8) ; + if (option[OPT_PHASE_REFRESH]) + sphase[ch] = 0; + envelope_write[ch] = true; + if (enable[ch]) + { + length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + sweep_sqr(ch); + break; + + default: + return false; + } + reg[adr] = val; + return true; + } + else if (adr == 0x4015) + { + enable[0] = (val & 1) ? true : false; + enable[1] = (val & 2) ? true : false; + + if (!enable[0]) + length_counter[0] = 0; + if (!enable[1]) + length_counter[1] = 0; + + reg[adr-0x4000] = val; + return true; + } + + // 4017 is handled in nes_dmc.cpp + //else if (adr == 0x4017) + //{ + //} + + return false; + } +} // namespace xgm; diff --git a/extern/NSFplay/nes_apu.h b/extern/NSFplay/nes_apu.h index 2a8e75412..a6caa282b 100644 --- a/extern/NSFplay/nes_apu.h +++ b/extern/NSFplay/nes_apu.h @@ -22,7 +22,7 @@ namespace xgm { SQR0_MASK = 1, SQR1_MASK = 2, }; protected: - int option[OPT_END]; // ŠeŽíƒIƒvƒVƒ‡ƒ“ + int option[OPT_END]; // å„種オプション int mask; INT32 sm[2][2]; @@ -66,7 +66,7 @@ namespace xgm public: NES_APU (); - ~NES_APU (); + ‾NES_APU (); void FrameSequence(int s); diff --git a/extern/NSFplay/nes_dmc.cpp b/extern/NSFplay/nes_dmc.cpp index 8073a22ca..49a58eb5e 100644 --- a/extern/NSFplay/nes_dmc.cpp +++ b/extern/NSFplay/nes_dmc.cpp @@ -67,7 +67,7 @@ namespace xgm } - NES_DMC::~NES_DMC () + NES_DMC::‾NES_DMC () { } @@ -122,7 +122,7 @@ namespace xgm void NES_DMC::FrameSequence(int s) { - //DEBUG_OUT("FrameSequence: %d\n",s); + //DEBUG_OUT("FrameSequence: %dÂ¥n",s); if (s > 3) return; // no operation in step 4 @@ -193,7 +193,7 @@ namespace xgm } - // ŽOŠp”gƒ`ƒƒƒ“ƒlƒ‹‚ÌŒvŽZ –ß‚è’l‚Í0-15 + // 三角波ãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-15 UINT32 NES_DMC::calc_tri (UINT32 clocks) { static UINT32 tritbl[32] = @@ -219,10 +219,10 @@ namespace xgm return ret; } - // ƒmƒCƒYƒ`ƒƒƒ“ƒlƒ‹‚ÌŒvŽZ –ß‚è’l‚Í0-127 - // ’áƒTƒ“ƒvƒŠƒ“ƒOƒŒ[ƒg‚Ҭ‚·‚邯ƒGƒCƒŠƒAƒXƒmƒCƒY‚ªŒƒ‚µ‚¢‚̂Š- // ƒmƒCƒY‚¾‚¯‚Í‚±‚ÌŠÖ”“à‚Å‚ƒNƒƒbƒN‡¬‚µAŠÈˆÕ‚ȃTƒ“ƒvƒŠƒ“ƒOƒŒ[ƒg - // •ÏŠ·‚ðs‚Á‚Ä‚¢‚éB + // ノイズãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-127 + // 低サンプリングレートã§åˆæˆã™ã‚‹ã¨ã‚¨ã‚¤ãƒªã‚¢ã‚¹ãƒŽã‚¤ã‚ºãŒæ¿€ã—ã„ã®ã§ + // ノイズã ã‘ã¯ã“ã®é–¢æ•°å†…ã§é«˜ã‚¯ãƒ­ãƒƒã‚¯åˆæˆã—ã€ç°¡æ˜“ãªã‚µãƒ³ãƒ—リングレート + // 変æ›ã‚’行ã£ã¦ã„る。 UINT32 NES_DMC::calc_noise(UINT32 clocks) { UINT32 env = envelope_disable ? noise_volume : envelope_counter; @@ -620,7 +620,7 @@ namespace xgm if (adr == 0x4017) { - //DEBUG_OUT("4017 = %02X\n", val); + //DEBUG_OUT("4017 = %02XÂ¥n", val); frame_irq_enable = ((val & 0x40) != 0x40); if (frame_irq_enable) frame_irq = false; cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); @@ -645,7 +645,7 @@ namespace xgm reg[adr-0x4008] = val&0xff; - //DEBUG_OUT("$%04X %02X\n", adr, val); + //DEBUG_OUT("$%04X %02XÂ¥n", adr, val); switch (adr) { @@ -724,12 +724,12 @@ namespace xgm case 0x4012: adr_reg = val&0xff; - // ‚±‚±‚Ådaddress‚ÍXV‚³‚ê‚È‚¢ + // ã“ã“ã§daddressã¯æ›´æ–°ã•れãªã„ break; case 0x4013: len_reg = val&0xff; - // ‚±‚±‚Ålength‚ÍXV‚³‚ê‚È‚¢ + // ã“ã“ã§lengthã¯æ›´æ–°ã•れãªã„ break; default: diff --git a/extern/NSFplay/nes_dmc.h b/extern/NSFplay/nes_dmc.h index 9152584aa..6cc8058a2 100644 --- a/extern/NSFplay/nes_dmc.h +++ b/extern/NSFplay/nes_dmc.h @@ -1,129 +1,129 @@ -#ifndef _NES_DMC_H_ -#define _NES_DMC_H_ - -#include "../device.h" -#include "../Audio/MedianFilter.h" -#include "../CPU/nes_cpu.h" - -namespace xgm -{ - class NES_APU; // forward declaration - - /** Bottom Half of APU **/ - class NES_DMC:public ISoundChip - { - public: - enum - { - OPT_ENABLE_4011=0, - OPT_ENABLE_PNOISE, - OPT_UNMUTE_ON_RESET, - OPT_DPCM_ANTI_CLICK, - OPT_NONLINEAR_MIXER, - OPT_RANDOMIZE_NOISE, - OPT_TRI_MUTE, - OPT_RANDOMIZE_TRI, - OPT_DPCM_REVERSE, - OPT_END - }; - protected: - const int GETA_BITS; - static const UINT32 freq_table[2][16]; - static const UINT32 wavlen_table[2][16]; - UINT32 tnd_table[2][16][16][128]; - - int option[OPT_END]; - int mask; - INT32 sm[2][3]; - UINT8 reg[0x10]; - UINT32 len_reg; - UINT32 adr_reg; - IDevice *memory; - UINT32 out[3]; - UINT32 daddress; - UINT32 dlength; - UINT32 data; - bool empty; - INT16 damp; - int dac_lsb; - bool dmc_pop; - INT32 dmc_pop_offset; - INT32 dmc_pop_follow; - double clock; - UINT32 rate; - int pal; - int mode; - bool irq; - - INT32 counter[3]; // frequency dividers - int tphase; // triangle phase - UINT32 nfreq; // noise frequency - UINT32 dfreq; // DPCM frequency - - UINT32 tri_freq; - int linear_counter; - int linear_counter_reload; - bool linear_counter_halt; - bool linear_counter_control; - - int noise_volume; - UINT32 noise, noise_tap; - - // noise envelope - bool envelope_loop; - bool envelope_disable; - bool envelope_write; - int envelope_div_period; - int envelope_div; - int envelope_counter; - - bool enable[2]; // tri/noise enable - int length_counter[2]; // 0=tri, 1=noise - - TrackInfoBasic trkinfo[3]; - - // frame sequencer - NES_APU* apu; // apu is clocked by DMC's frame sequencer - int frame_sequence_count; // current cycle count - int frame_sequence_length; // CPU cycles per FrameSequence - int frame_sequence_step; // current step of frame sequence - int frame_sequence_steps; // 4/5 steps per frame - bool frame_irq; - bool frame_irq_enable; - - NES_CPU* cpu; // IRQ needs CPU access - - inline UINT32 calc_tri (UINT32 clocks); - inline UINT32 calc_dmc (UINT32 clocks); - inline UINT32 calc_noise (UINT32 clocks); - - public: - NES_DMC (); - ~NES_DMC (); - - void InitializeTNDTable(double wt, double wn, double wd); - void SetPal (bool is_pal); - void SetAPU (NES_APU* apu_); - void SetMemory (IDevice * r); - void FrameSequence(int s); - int GetDamp(){ return (damp<<1)|dac_lsb ; } - void TickFrameSequence (UINT32 clocks); - - virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 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, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - - void SetCPU(NES_CPU* cpu_); - }; - -} - -#endif +#ifndef _NES_DMC_H_ +#define _NES_DMC_H_ + +#include "../device.h" +#include "../Audio/MedianFilter.h" +#include "../CPU/nes_cpu.h" + +namespace xgm +{ + class NES_APU; // forward declaration + + /** Bottom Half of APU **/ + class NES_DMC:public ISoundChip + { + public: + enum + { + OPT_ENABLE_4011=0, + OPT_ENABLE_PNOISE, + OPT_UNMUTE_ON_RESET, + OPT_DPCM_ANTI_CLICK, + OPT_NONLINEAR_MIXER, + OPT_RANDOMIZE_NOISE, + OPT_TRI_MUTE, + OPT_RANDOMIZE_TRI, + OPT_DPCM_REVERSE, + OPT_END + }; + protected: + const int GETA_BITS; + static const UINT32 freq_table[2][16]; + static const UINT32 wavlen_table[2][16]; + UINT32 tnd_table[2][16][16][128]; + + int option[OPT_END]; + int mask; + INT32 sm[2][3]; + UINT8 reg[0x10]; + UINT32 len_reg; + UINT32 adr_reg; + IDevice *memory; + UINT32 out[3]; + UINT32 daddress; + UINT32 dlength; + UINT32 data; + bool empty; + INT16 damp; + int dac_lsb; + bool dmc_pop; + INT32 dmc_pop_offset; + INT32 dmc_pop_follow; + double clock; + UINT32 rate; + int pal; + int mode; + bool irq; + + INT32 counter[3]; // frequency dividers + int tphase; // triangle phase + UINT32 nfreq; // noise frequency + UINT32 dfreq; // DPCM frequency + + UINT32 tri_freq; + int linear_counter; + int linear_counter_reload; + bool linear_counter_halt; + bool linear_counter_control; + + int noise_volume; + UINT32 noise, noise_tap; + + // noise envelope + bool envelope_loop; + bool envelope_disable; + bool envelope_write; + int envelope_div_period; + int envelope_div; + int envelope_counter; + + bool enable[2]; // tri/noise enable + int length_counter[2]; // 0=tri, 1=noise + + TrackInfoBasic trkinfo[3]; + + // frame sequencer + NES_APU* apu; // apu is clocked by DMC's frame sequencer + int frame_sequence_count; // current cycle count + int frame_sequence_length; // CPU cycles per FrameSequence + int frame_sequence_step; // current step of frame sequence + int frame_sequence_steps; // 4/5 steps per frame + bool frame_irq; + bool frame_irq_enable; + + NES_CPU* cpu; // IRQ needs CPU access + + inline UINT32 calc_tri (UINT32 clocks); + inline UINT32 calc_dmc (UINT32 clocks); + inline UINT32 calc_noise (UINT32 clocks); + + public: + NES_DMC (); + ‾NES_DMC (); + + void InitializeTNDTable(double wt, double wn, double wd); + void SetPal (bool is_pal); + void SetAPU (NES_APU* apu_); + void SetMemory (IDevice * r); + void FrameSequence(int s); + int GetDamp(){ return (damp<<1)|dac_lsb ; } + void TickFrameSequence (UINT32 clocks); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 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, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + + void SetCPU(NES_CPU* cpu_); + }; + +} + +#endif diff --git a/extern/NSFplay/nes_fds.cpp b/extern/NSFplay/nes_fds.cpp index 9d74c62f0..1e08100c0 100644 --- a/extern/NSFplay/nes_fds.cpp +++ b/extern/NSFplay/nes_fds.cpp @@ -22,7 +22,7 @@ NES_FDS::NES_FDS () Reset(); } -NES_FDS::~NES_FDS () +NES_FDS::‾NES_FDS () { } diff --git a/extern/NSFplay/nes_fds.h b/extern/NSFplay/nes_fds.h index 212e472b4..67c60fd1c 100644 --- a/extern/NSFplay/nes_fds.h +++ b/extern/NSFplay/nes_fds.h @@ -63,7 +63,7 @@ protected: public: NES_FDS (); - virtual ~ NES_FDS (); + virtual ‾ NES_FDS (); virtual void Reset (); virtual void Tick (UINT32 clocks); diff --git a/extern/NSFplay/nes_fme7.cpp b/extern/NSFplay/nes_fme7.cpp deleted file mode 100644 index 9d48de794..000000000 --- a/extern/NSFplay/nes_fme7.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "nes_fme7.h" - -using namespace xgm; - -const int DIVIDER = 8; // TODO this is not optimal, rewrite PSG output - -NES_FME7::NES_FME7 () -{ - psg = PSG_new ((e_uint32)DEFAULT_CLOCK, DEFAULT_RATE); - divider = 0; - - for(int c=0;c<2;++c) - for(int t=0;t<3;++t) - sm[c][t] = 128; -} - -NES_FME7::~NES_FME7 () -{ - if (psg) - PSG_delete (psg); -} - -void NES_FME7::SetClock (double c) -{ - this->clock = c * 2.0; -} - -void NES_FME7::SetRate (double r) -{ - //rate = r ? r : DEFAULT_RATE; - rate = DEFAULT_CLOCK / double(DIVIDER); // TODO rewrite PSG to integrate with clock - if (psg) - PSG_set_rate (psg, (e_uint32)rate); -} - -void NES_FME7::SetOption (int id, int val) -{ - if(id= DIVIDER) - { - divider -= DIVIDER; - if (psg) PSG_calc(psg); - } -} - -xgm::UINT32 NES_FME7::Render (xgm::INT32 b[2]) -{ - b[0] = b[1] = 0; - - for (int i=0; i < 3; ++i) - { - // note negative polarity - b[0] -= psg->cout[i] * sm[0][i]; - b[1] -= psg->cout[i] * sm[1][i]; - } - b[0] >>= (7-4); - b[1] >>= (7-4); - - // master volume adjustment - const INT32 MASTER = INT32(0.64 * 256.0); - b[0] = (b[0] * MASTER) >> 8; - b[1] = (b[1] * MASTER) >> 8; - - return 2; -} - -void NES_FME7::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) -{ - if (trk < 0) return; - if (trk > 2) return; - sm[0][trk] = mixl; - sm[1][trk] = mixr; -} - -ITrackInfo *NES_FME7::GetTrackInfo(int trk) -{ - assert(trk<5); - - if(psg) - { - if (trk<3) - { - trkinfo[trk]._freq = psg->freq[trk]; - if(psg->freq[trk]) - trkinfo[trk].freq = psg->clk/32.0/psg->freq[trk]; - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = psg->cout[trk]; - trkinfo[trk].max_volume = 15; - trkinfo[trk].volume = psg->volume[trk] >> 1; - //trkinfo[trk].key = (psg->cout[trk]>0)?true:false; - trkinfo[trk].key = !(psg->tmask[trk]); - trkinfo[trk].tone = (psg->tmask[trk]?2:0)+(psg->nmask[trk]?1:0); - } - else if (trk == 3) // envelope - { - trkinfo[trk]._freq = psg->env_freq; - if(psg->env_freq) - trkinfo[trk].freq = psg->clk/512.0/psg->env_freq; - else - trkinfo[trk].freq = 0; - - if (psg->env_continue && psg->env_alternate && !psg->env_hold) // triangle wave - { - trkinfo[trk].freq *= 0.5f; // sounds an octave down - } - - trkinfo[trk].output = psg->voltbl[psg->env_ptr]; - trkinfo[trk].max_volume = 0; - trkinfo[trk].volume = 0; - trkinfo[trk].key = (((psg->volume[0]|psg->volume[1]|psg->volume[2])&32) != 0); - trkinfo[trk].tone = - (psg->env_continue ?8:0) | - (psg->env_attack ?4:0) | - (psg->env_alternate?2:0) | - (psg->env_hold ?1:0) ; - } - else if (trk == 4) // noise - { - trkinfo[trk]._freq = psg->noise_freq >> 1; - if(trkinfo[trk]._freq > 0) - trkinfo[trk].freq = psg->clk/16.0/psg->noise_freq; - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = psg->noise_seed & 1; - trkinfo[trk].max_volume = 0; - trkinfo[trk].volume = 0; - //trkinfo[trk].key = ((psg->nmask[0]&psg->nmask[1]&psg->nmask[2]) == 0); - trkinfo[trk].key = false; - trkinfo[trk].tone = 0; - } - } - return &trkinfo[trk]; -} diff --git a/extern/NSFplay/nes_fme7.h b/extern/NSFplay/nes_fme7.h deleted file mode 100644 index d351b0859..000000000 --- a/extern/NSFplay/nes_fme7.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _NES_FME7_H_ -#define _NES_FME7_H_ -#include "../device.h" -#include "legacy/emu2149.h" - -namespace xgm -{ - - class NES_FME7:public ISoundChip - { - public: - enum - { - OPT_END - }; - protected: - //int option[OPT_END]; - INT32 sm[2][3]; // stereo mix - INT16 buf[2]; - PSG *psg; - int divider; // clock divider - double clock, rate; - TrackInfoBasic trkinfo[5]; - public: - NES_FME7 (); - ~NES_FME7 (); - virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual void SetClock (double); - virtual void SetRate (double); - virtual void SetOption (int, int); - virtual void SetMask (int m){ if(psg) PSG_setMask(psg,m); } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - }; - -} // namespace - -#endif diff --git a/extern/NSFplay/nes_mmc5.cpp b/extern/NSFplay/nes_mmc5.cpp index 2b2f8fcb7..9243f6595 100644 --- a/extern/NSFplay/nes_mmc5.cpp +++ b/extern/NSFplay/nes_mmc5.cpp @@ -35,7 +35,7 @@ namespace xgm sm[c][t] = 128; } - NES_MMC5::~NES_MMC5 () + NES_MMC5::‾NES_MMC5 () { } diff --git a/extern/NSFplay/nes_mmc5.h b/extern/NSFplay/nes_mmc5.h index b05fbb506..5747ab0d6 100644 --- a/extern/NSFplay/nes_mmc5.h +++ b/extern/NSFplay/nes_mmc5.h @@ -31,8 +31,8 @@ namespace xgm INT32 out[3]; bool enable[2]; - bool envelope_disable[2]; // ƒGƒ“ƒxƒ[ƒv—LŒøƒtƒ‰ƒO - bool envelope_loop[2]; // ƒGƒ“ƒxƒ[ƒvƒ‹[ƒv + bool envelope_disable[2]; // エンベロープ有効フラグ + bool envelope_loop[2]; // エンベロープループ bool envelope_write[2]; int envelope_div_period[2]; int envelope_div[2]; @@ -49,7 +49,7 @@ namespace xgm TrackInfoBasic trkinfo[3]; public: NES_MMC5 (); - ~NES_MMC5 (); + ‾NES_MMC5 (); void FrameSequence (); void TickFrameSequence (UINT32 clocks); diff --git a/extern/NSFplay/nes_n106.cpp b/extern/NSFplay/nes_n106.cpp index f53d4ae7c..ae793dc08 100644 --- a/extern/NSFplay/nes_n106.cpp +++ b/extern/NSFplay/nes_n106.cpp @@ -18,7 +18,7 @@ NES_N106::NES_N106 () Reset(); } -NES_N106::~NES_N106 () +NES_N106::‾NES_N106 () { } diff --git a/extern/NSFplay/nes_n106.h b/extern/NSFplay/nes_n106.h index 82d7eaa6a..aebb6cb46 100644 --- a/extern/NSFplay/nes_n106.h +++ b/extern/NSFplay/nes_n106.h @@ -54,7 +54,7 @@ protected: public: NES_N106 (); - ~NES_N106 (); + ‾NES_N106 (); virtual void Reset (); virtual void Tick (UINT32 clocks); diff --git a/extern/NSFplay/nes_vrc6.cpp b/extern/NSFplay/nes_vrc6.cpp index 6a19b5ea5..c46109403 100644 --- a/extern/NSFplay/nes_vrc6.cpp +++ b/extern/NSFplay/nes_vrc6.cpp @@ -16,7 +16,7 @@ namespace xgm sm[c][t] = 128; } - NES_VRC6::~NES_VRC6 () + NES_VRC6::‾NES_VRC6 () { } diff --git a/extern/NSFplay/nes_vrc6.h b/extern/NSFplay/nes_vrc6.h index 2e277e6ee..2bb64eb8a 100644 --- a/extern/NSFplay/nes_vrc6.h +++ b/extern/NSFplay/nes_vrc6.h @@ -36,7 +36,7 @@ namespace xgm public: NES_VRC6 (); - ~NES_VRC6 (); + ‾NES_VRC6 (); virtual void Reset (); virtual void Tick (UINT32 clocks); diff --git a/extern/NSFplay/nes_vrc7.cpp b/extern/NSFplay/nes_vrc7.cpp deleted file mode 100644 index 4123fb36b..000000000 --- a/extern/NSFplay/nes_vrc7.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include -#include "nes_vrc7.h" - -namespace xgm -{ - NES_VRC7::NES_VRC7 () - { - use_all_channels = false; - patch_set = OPLL_VRC7_TONE; - patch_custom = NULL; - divider = 0; - - opll = OPLL_new ( 3579545, DEFAULT_RATE); - OPLL_reset_patch (opll, patch_set); - SetClock(DEFAULT_CLOCK); - - for(int c=0;c<2;++c) - //for(int t=0;t<6;++t) - for(int t=0;t<9;++t) // HACK for YM2413 support - sm[c][t] = 128; - } - - NES_VRC7::~NES_VRC7 () - { - OPLL_delete (opll); - } - - void NES_VRC7::UseAllChannels(bool b) - { - use_all_channels = b; - } - - void NES_VRC7::SetPatchSet(int p) - { - patch_set = p; - } - - void NES_VRC7::SetPatchSetCustom (const UINT8* pset) - { - patch_custom = pset; - } - - void NES_VRC7::SetClock (double c) - { - clock = c / 36; - } - - void NES_VRC7::SetRate (double r) - { - //rate = r ? r : DEFAULT_RATE; - (void)r; // rate is ignored - rate = 49716; - OPLL_set_quality(opll, 1); // quality always on (not really a CPU hog) - OPLL_set_rate(opll,(uint32_t)rate); - } - - void NES_VRC7::SetOption (int id, int val) - { - if(id 5) return; - if (trk > 8) return; // HACK YM2413 - sm[0][trk] = mixl; - sm[1][trk] = mixr; - } - - ITrackInfo *NES_VRC7::GetTrackInfo(int trk) - { - //if(opll&&trk<6) - if(opll&&trk<9) // HACK YM2413 (percussion mode isn't very diagnostic this way though) - { - trkinfo[trk].max_volume = 15; - trkinfo[trk].volume = 15 - ((opll->reg[0x30+trk])&15); - trkinfo[trk]._freq = opll->reg[0x10+trk]+((opll->reg[0x20+trk]&1)<<8); - int blk = (opll->reg[0x20+trk]>>1)&7; - trkinfo[trk].freq = clock*trkinfo[trk]._freq/(double)(0x80000>>blk); - trkinfo[trk].tone = (opll->reg[0x30+trk]>>4)&15; - //trkinfo[trk].key = (opll->reg[0x20+trk]&0x10)?true:false; - trkinfo[trk].key = opll->slot_key_status & (3 << trk)?true:false; - return &trkinfo[trk]; - } - else - return NULL; - } - - bool NES_VRC7::Write (UINT32 adr, UINT32 val, UINT32 id) - { - if (adr == 0x9010) - { - OPLL_writeIO (opll, 0, val); - return true; - } - if (adr == 0x9030) - { - OPLL_writeIO (opll, 1, val); - return true; - } - else - return false; - } - - bool NES_VRC7::Read (UINT32 adr, UINT32 & val, UINT32 id) - { - return false; - } - - void NES_VRC7::Tick (UINT32 clocks) - { - divider += clocks; - while (divider >= 36) - { - divider -= 36; - OPLL_calc(opll); - } - } - - UINT32 NES_VRC7::Render (INT32 b[2]) - { - b[0] = b[1] = 0; - for (int i=0; i < 6; ++i) - { - INT32 val = (mask & (1<ch_out[i] >> 4; - b[0] += val * sm[0][i]; - b[1] += val * sm[1][i]; - } - - // HACK for YM2413 support - if (use_all_channels) - { - for (int i=6; i < 9; ++i) - { - if (mask & (1<patch_number[i] > 15) // rhytm mode - { - if (i == 6) val = opll->ch_out[9]; // BD - else if (i == 7) val = opll->ch_out[10] + opll->ch_out[11]; // HH&SD - else val = opll->ch_out[12] + opll->ch_out[13]; // TOM&CYM - /* (i == 8) is implied */ - } - else - { - val = opll->ch_out[i]; - } - val >>= 4; - b[0] += val * sm[0][i]; - b[1] += val * sm[1][i]; - } - } - - b[0] >>= (7 - 4); - b[1] >>= (7 - 4); - - // master volume adjustment - const INT32 MASTER = INT32(1.15 * 256.0); - b[0] = (b[0] * MASTER) >> 8; - b[1] = (b[1] * MASTER) >> 8; - - return 2; - } -} diff --git a/extern/NSFplay/nes_vrc7.h b/extern/NSFplay/nes_vrc7.h deleted file mode 100644 index e8a91d61d..000000000 --- a/extern/NSFplay/nes_vrc7.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef _NES_VRC7_H_ -#define _NES_VRC7_H_ -#include "../device.h" -#include "legacy/emu2413.h" - -namespace xgm -{ - - class NES_VRC7 : public ISoundChip - { - public: - enum - { - OPT_OPLL=0, - OPT_END - }; - protected: - int option[OPT_END]; - int mask; - int patch_set; - const UINT8* patch_custom; - //INT32 sm[2][6]; // stereo mix - INT32 sm[2][9]; // stereo mix temporary HACK to support YM2413 - INT16 buf[2]; - OPLL *opll; - UINT32 divider; // clock divider - double clock, rate; - //TrackInfoBasic trkinfo[6]; - TrackInfoBasic trkinfo[9]; // HACK to support YM2413 - bool use_all_channels; - public: - NES_VRC7 (); - ~NES_VRC7 (); - - virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual void UseAllChannels (bool b); - virtual void SetPatchSet (int p); - virtual void SetPatchSetCustom (const UINT8* pset); - virtual void SetClock (double); - virtual void SetRate (double); - virtual void SetOption (int, int); - virtual void SetMask (int m){ mask = m; if(opll) OPLL_setMask(opll, m); } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - }; - -} // namespace - -#endif From 867f96ff0166de4dec4fa4b22053aaa305978cb5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 22:19:00 -0500 Subject: [PATCH 08/26] add readme --- extern/NSFplay/readme.txt | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 extern/NSFplay/readme.txt diff --git a/extern/NSFplay/readme.txt b/extern/NSFplay/readme.txt new file mode 100644 index 000000000..f3a625d90 --- /dev/null +++ b/extern/NSFplay/readme.txt @@ -0,0 +1,60 @@ +MODIFIED + +this is a modified version of the NES audio emulation core. +it converts the files to UTF-8 and Unix line endings. + +XGM SOURCE ARCHIVE + +This source archive is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. You can reuse these source code freely. However, +we possibly change the structure and interface of the program code without +any advance notice. + +HOW TO COMPILE + +Open the workspace file top.sln with Visual C++ 7.0 or later +version. We can compile both KbMediaPlayer and Winamp version of +NSFplug on this workspace. + +To make a KbMediaPlayer version of NSFplug, that is, in_nsf.kpi, +Please choose 'kbnsf' project as an active project. Then, you can +build in_nsf.kpi by build menu. + +On the other hand, to make a Winamp version of NSFplug, activate +'wa2nsf' project and build it. + +Note that after the build process, VC++ copies the plugin files to: + +C:\Program Files\KbMediaPlayer\Plugins\OK\in_nsf\in_nsf.kpi +C:\Program Files\Windamp\Plugins\in_nsf.dll + +If you don't need to have these copies, please remove or modify the + custom build settings. + +ACKNOWLEDGEMENT + +I thank Mamiya and Kobarin and Nullsoft for their great source code. +I thank Norix and Izumi for the fruitful discussions and the NSFplug +users for their comments and bug reports. + +COPYRIGHTS + +NSFplug is built on KM6502, KbMediaPlayer plugin SDK and Winamp2 +plugin SDK. + +NSFplug uses KM6502 in emulating a 6502 cpu. KM6502 code is stored in +devices\CPU\km6502 folder of this source archive. KM6502 is a public +domain software. See the document of KM6502 stored in the folder. + +KbMediaPlayer Plugin SDK is provided by Kobarin. The SDK code is also +packed in the kbmedia\sdk folder of this archive. The copyright of +the source code remains with Kobarin. + +The files in winamp/sdk folder of this archive are the header files +from Winamp2 Plugin SDK provided by Nullsoft. The copyright of these +header files remains with Nullsoft. + +CONTACT + +Digital Sound Antiques +http://dsa.sakura.ne.jp/ From e873070d8444efd6edfab230edfa1f509013e6a6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 22:22:02 -0500 Subject: [PATCH 09/26] now move these files --- .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_apu.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_apu.h | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_dmc.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_dmc.h | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_fds.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_fds.h | 0 .../engine/platform/sound/nes_nsfplay}/nes_mmc5.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_mmc5.h | 0 .../engine/platform/sound/nes_nsfplay}/nes_n106.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_n106.h | 0 .../engine/platform/sound/nes_nsfplay}/nes_vrc6.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_vrc6.h | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/readme.txt | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_apu.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_apu.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_dmc.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_dmc.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_fds.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_fds.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_mmc5.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_mmc5.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_n106.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_n106.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_vrc6.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_vrc6.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/readme.txt (100%) diff --git a/extern/NSFplay/nes_apu.cpp b/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp similarity index 100% rename from extern/NSFplay/nes_apu.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_apu.cpp diff --git a/extern/NSFplay/nes_apu.h b/src/engine/platform/sound/nes_nsfplay/nes_apu.h similarity index 100% rename from extern/NSFplay/nes_apu.h rename to src/engine/platform/sound/nes_nsfplay/nes_apu.h diff --git a/extern/NSFplay/nes_dmc.cpp b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp similarity index 100% rename from extern/NSFplay/nes_dmc.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp diff --git a/extern/NSFplay/nes_dmc.h b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h similarity index 100% rename from extern/NSFplay/nes_dmc.h rename to src/engine/platform/sound/nes_nsfplay/nes_dmc.h diff --git a/extern/NSFplay/nes_fds.cpp b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp similarity index 100% rename from extern/NSFplay/nes_fds.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_fds.cpp diff --git a/extern/NSFplay/nes_fds.h b/src/engine/platform/sound/nes_nsfplay/nes_fds.h similarity index 100% rename from extern/NSFplay/nes_fds.h rename to src/engine/platform/sound/nes_nsfplay/nes_fds.h diff --git a/extern/NSFplay/nes_mmc5.cpp b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp similarity index 100% rename from extern/NSFplay/nes_mmc5.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp diff --git a/extern/NSFplay/nes_mmc5.h b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h similarity index 100% rename from extern/NSFplay/nes_mmc5.h rename to src/engine/platform/sound/nes_nsfplay/nes_mmc5.h diff --git a/extern/NSFplay/nes_n106.cpp b/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp similarity index 100% rename from extern/NSFplay/nes_n106.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_n106.cpp diff --git a/extern/NSFplay/nes_n106.h b/src/engine/platform/sound/nes_nsfplay/nes_n106.h similarity index 100% rename from extern/NSFplay/nes_n106.h rename to src/engine/platform/sound/nes_nsfplay/nes_n106.h diff --git a/extern/NSFplay/nes_vrc6.cpp b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp similarity index 100% rename from extern/NSFplay/nes_vrc6.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp diff --git a/extern/NSFplay/nes_vrc6.h b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h similarity index 100% rename from extern/NSFplay/nes_vrc6.h rename to src/engine/platform/sound/nes_nsfplay/nes_vrc6.h diff --git a/extern/NSFplay/readme.txt b/src/engine/platform/sound/nes_nsfplay/readme.txt similarity index 100% rename from extern/NSFplay/readme.txt rename to src/engine/platform/sound/nes_nsfplay/readme.txt From 7b31f6a3e57b3b2aeedc55d23986a9d90559d618 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 22:52:22 -0500 Subject: [PATCH 10/26] now fix it damn it --- CMakeLists.txt | 7 + .../platform/sound/nes_nsfplay/common.h | 4 + .../platform/sound/nes_nsfplay/nes_apu.cpp | 50 +++---- .../platform/sound/nes_nsfplay/nes_apu.h | 33 +++-- .../platform/sound/nes_nsfplay/nes_dmc.cpp | 124 +++++------------- .../platform/sound/nes_nsfplay/nes_dmc.h | 78 +++++------ .../platform/sound/nes_nsfplay/nes_fds.cpp | 64 ++++----- .../platform/sound/nes_nsfplay/nes_fds.h | 58 ++++---- .../platform/sound/nes_nsfplay/nes_mmc5.cpp | 88 +++---------- .../platform/sound/nes_nsfplay/nes_mmc5.h | 51 ++++--- .../platform/sound/nes_nsfplay/nes_n106.cpp | 81 ++++-------- .../platform/sound/nes_nsfplay/nes_n106.h | 44 +++---- .../platform/sound/nes_nsfplay/nes_vrc6.cpp | 49 ++----- .../platform/sound/nes_nsfplay/nes_vrc6.h | 33 +++-- 14 files changed, 268 insertions(+), 496 deletions(-) create mode 100644 src/engine/platform/sound/nes_nsfplay/common.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b757062f2..a881bad60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,6 +250,13 @@ src/engine/platform/sound/nes/mmc5.c src/engine/platform/sound/vera_psg.c src/engine/platform/sound/vera_pcm.c +src/engine/platform/sound/nes_nsfplay/nes_apu.cpp +src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp +src/engine/platform/sound/nes_nsfplay/nes_fds.cpp +src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp +src/engine/platform/sound/nes_nsfplay/nes_n106.cpp +src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp + src/engine/platform/sound/c64/sid.cc src/engine/platform/sound/c64/voice.cc src/engine/platform/sound/c64/wave.cc diff --git a/src/engine/platform/sound/nes_nsfplay/common.h b/src/engine/platform/sound/nes_nsfplay/common.h new file mode 100644 index 000000000..894a69737 --- /dev/null +++ b/src/engine/platform/sound/nes_nsfplay/common.h @@ -0,0 +1,4 @@ +namespace xgm { + const unsigned int DEFAULT_CLOCK=1789773; + const unsigned int DEFAULT_RATE=1789773; +}; diff --git a/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp b/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp index b2ebb677a..f215a9aa6 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp @@ -3,6 +3,7 @@ // #include #include "nes_apu.h" +#include "common.h" namespace xgm { @@ -86,9 +87,9 @@ namespace xgm } - INT32 NES_APU::calc_sqr (int i, UINT32 clocks) + int NES_APU::calc_sqr (int i, unsigned int clocks) { - static const INT16 sqrtbl[4][16] = { + static const short sqrtbl[4][16] = { {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, @@ -102,7 +103,7 @@ namespace xgm scounter[i] += freq[i] + 1; } - INT32 ret = 0; + int ret = 0; if (length_counter[i] > 0 && freq[i] >= 8 && sfreq[i] < 0x800 @@ -115,7 +116,7 @@ namespace xgm return ret; } - bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) + bool NES_APU::Read (unsigned int adr, unsigned int & val, unsigned int id) { if (0x4000 <= adr && adr < 0x4008) { @@ -131,26 +132,26 @@ namespace xgm return false; } - void NES_APU::Tick (UINT32 clocks) + void NES_APU::Tick (unsigned int clocks) { out[0] = calc_sqr(0, clocks); out[1] = calc_sqr(1, clocks); } // ツé’カツé’ャツã¤ï½³ï¾‚ã¥ï½ªï¾‚ã¥ï½©ï¾‚波ツ形ツã¥å€‹æŒ¯ï¾‚閉敖ã¥0-8191 - UINT32 NES_APU::Render (INT32 b[2]) + unsigned int NES_APU::Render (int b[2]) { out[0] = (mask & 1) ? 0 : out[0]; out[1] = (mask & 2) ? 0 : out[1]; - INT32 m[2]; + int m[2]; if(option[OPT_NONLINEAR_MIXER]) { - INT32 voltage = square_table[out[0] + out[1]]; + int voltage = square_table[out[0] + out[1]]; m[0] = out[0] << 6; m[1] = out[1] << 6; - INT32 ref = m[0] + m[1]; + int ref = m[0] + m[1]; if (ref > 0) { m[0] = (m[0] * voltage) / ref; @@ -191,7 +192,7 @@ namespace xgm square_table[0] = 0; for(int i=1;i<32;i++) - square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + square_table[i]=(int)((8192.0*95.88)/(8128.0/i+100)); square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear @@ -200,7 +201,7 @@ namespace xgm sm[c][t] = 128; } - NES_APU::‾NES_APU () + NES_APU::~NES_APU () { } @@ -267,7 +268,7 @@ namespace xgm rate = r ? r : DEFAULT_RATE; } - void NES_APU::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + void NES_APU::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 1) return; @@ -275,32 +276,11 @@ namespace xgm sm[1][trk] = mixr; } - ITrackInfo *NES_APU::GetTrackInfo(int trk) - { - trkinfo[trk]._freq = freq[trk]; - if(freq[trk]) - trkinfo[trk].freq = clock/16/(freq[trk] + 1); - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = out[trk]; - trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); - trkinfo[trk].key = - enable[trk] && - length_counter[trk] > 0 && - freq[trk] >= 8 && - sfreq[trk] < 0x800 && - (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); - trkinfo[trk].tone = duty[trk]; - trkinfo[trk].max_volume = 15; - return &trkinfo[trk]; - } - - bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) + bool NES_APU::Write (unsigned int adr, unsigned int val, unsigned int id) { int ch; - static const UINT8 length_table[32] = { + static const unsigned char length_table[32] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, diff --git a/src/engine/platform/sound/nes_nsfplay/nes_apu.h b/src/engine/platform/sound/nes_nsfplay/nes_apu.h index a6caa282b..004c99974 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_apu.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_apu.h @@ -1,12 +1,11 @@ #ifndef _NES_APU_H_ #define _NES_APU_H_ -#include "../device.h" #include "nes_dmc.h" namespace xgm { /** Upper half of APU **/ - class NES_APU : public ISoundChip + class NES_APU { public: enum @@ -24,15 +23,15 @@ namespace xgm protected: int option[OPT_END]; // å„種オプション int mask; - INT32 sm[2][2]; + int sm[2][2]; - UINT32 gclock; - UINT8 reg[0x20]; - INT32 out[2]; + unsigned int gclock; + unsigned char reg[0x20]; + int out[2]; double rate, clock; - INT32 square_table[32]; // nonlinear mixer - INT32 square_linear; // linear mix approximation + int square_table[32]; // nonlinear mixer + int square_linear; // linear mix approximation int scounter[2]; // frequency divider int sphase[2]; // phase counter @@ -61,26 +60,24 @@ namespace xgm bool enable[2]; void sweep_sqr (int ch); // calculates target sweep frequency - INT32 calc_sqr (int ch, UINT32 clocks); - TrackInfoBasic trkinfo[2]; + int calc_sqr (int ch, unsigned int clocks); public: - NES_APU (); - ‾NES_APU (); + NES_APU (); + ~NES_APU (); void FrameSequence(int s); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + 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, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp index 49a58eb5e..a37b544d2 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp @@ -1,10 +1,12 @@ #include "nes_dmc.h" #include "nes_apu.h" +#include "common.h" +#include #include namespace xgm { - const UINT32 NES_DMC::wavlen_table[2][16] = { + const unsigned int NES_DMC::wavlen_table[2][16] = { { // NTSC 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 }, @@ -12,7 +14,7 @@ namespace xgm 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 }}; - const UINT32 NES_DMC::freq_table[2][16] = { + const unsigned int NES_DMC::freq_table[2][16] = { { // NTSC 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 }, @@ -20,7 +22,7 @@ namespace xgm 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 }}; - const UINT32 BITREVERSE[256] = { + const unsigned int BITREVERSE[256] = { 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, @@ -67,11 +69,11 @@ namespace xgm } - NES_DMC::‾NES_DMC () + NES_DMC::~NES_DMC () { } - void NES_DMC::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + void NES_DMC::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 2) return; @@ -79,47 +81,6 @@ namespace xgm sm[1][trk] = mixr; } - ITrackInfo *NES_DMC::GetTrackInfo(int trk) - { - switch(trk) - { - case 0: - trkinfo[trk].max_volume = 255; - trkinfo[0].key = (linear_counter>0 && length_counter[0]>0 && enable[0]); - trkinfo[0].volume = 0; - trkinfo[0]._freq = tri_freq; - if(trkinfo[0]._freq) - trkinfo[0].freq = clock/32/(trkinfo[0]._freq + 1); - else - trkinfo[0].freq = 0; - trkinfo[0].tone = -1; - trkinfo[0].output = out[0]; - break; - case 1: - trkinfo[1].max_volume = 15; - trkinfo[1].volume = noise_volume+(envelope_disable?0:0x10)+(envelope_loop?0x20:0); - trkinfo[1].key = length_counter[1]>0 && enable[1] && - (envelope_disable ? (noise_volume>0) : (envelope_counter>0)); - trkinfo[1]._freq = reg[0x400e - 0x4008]&0xF; - trkinfo[1].freq = clock/double(wavlen_table[pal][trkinfo[1]._freq] * ((noise_tap&(1<<6)) ? 93 : 1)); - trkinfo[1].tone = noise_tap & (1<<6); - trkinfo[1].output = out[1]; - break; - case 2: - trkinfo[2].max_volume = 127; - trkinfo[2].volume = reg[0x4011 - 0x4008]&0x7F; - trkinfo[2].key = dlength > 0; - trkinfo[2]._freq = reg[0x4010 - 0x4008]&0xF; - trkinfo[2].freq = clock/double(freq_table[pal][trkinfo[2]._freq]); - trkinfo[2].tone = (0xc000|(adr_reg<<6)); - trkinfo[2].output = (damp<<1)|dac_lsb; - break; - default: - return NULL; - } - return &trkinfo[trk]; - } - void NES_DMC::FrameSequence(int s) { //DEBUG_OUT("FrameSequence: %dÂ¥n",s); @@ -134,7 +95,6 @@ namespace xgm if (s == 0 && (frame_sequence_steps == 4)) { if (frame_irq_enable) frame_irq = true; - cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, frame_irq & frame_irq_enable); } // 240hz clock @@ -194,9 +154,9 @@ namespace xgm } // 三角波ãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-15 - UINT32 NES_DMC::calc_tri (UINT32 clocks) + unsigned int NES_DMC::calc_tri (unsigned int clocks) { - static UINT32 tritbl[32] = + static unsigned int tritbl[32] = { 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, @@ -215,7 +175,7 @@ namespace xgm } } - UINT32 ret = tritbl[tphase]; + unsigned int ret = tritbl[tphase]; return ret; } @@ -223,20 +183,20 @@ namespace xgm // 低サンプリングレートã§åˆæˆã™ã‚‹ã¨ã‚¨ã‚¤ãƒªã‚¢ã‚¹ãƒŽã‚¤ã‚ºãŒæ¿€ã—ã„ã®ã§ // ノイズã ã‘ã¯ã“ã®é–¢æ•°å†…ã§é«˜ã‚¯ãƒ­ãƒƒã‚¯åˆæˆã—ã€ç°¡æ˜“ãªã‚µãƒ³ãƒ—リングレート // 変æ›ã‚’行ã£ã¦ã„る。 - UINT32 NES_DMC::calc_noise(UINT32 clocks) + unsigned int NES_DMC::calc_noise(unsigned int clocks) { - UINT32 env = envelope_disable ? noise_volume : envelope_counter; + unsigned int env = envelope_disable ? noise_volume : envelope_counter; if (length_counter[1] < 1) env = 0; - UINT32 last = (noise & 0x4000) ? 0 : env; + unsigned int last = (noise & 0x4000) ? 0 : env; if (clocks < 1) return last; // simple anti-aliasing (noise requires it, even when oversampling is off) - UINT32 count = 0; - UINT32 accum = counter[1] * last; // samples pending from previous calc - UINT32 accum_clocks = counter[1]; + unsigned int count = 0; + unsigned int accum = counter[1] * last; // samples pending from previous calc + unsigned int accum_clocks = counter[1]; #ifdef _DEBUG - INT32 start_clocks = counter[1]; + int start_clocks = counter[1]; #endif if (counter[1] < 0) // only happens on startup when using the randomize noise option { @@ -249,7 +209,7 @@ namespace xgm while (counter[1] < 0) { // tick the noise generator - UINT32 feedback = (noise&1) ^ ((noise&noise_tap)?1:0); + unsigned int feedback = (noise&1) ^ ((noise&noise_tap)?1:0); noise = (noise>>1) | (feedback<<14); last = (noise & 0x4000) ? 0 : env; @@ -270,13 +230,13 @@ namespace xgm if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal #endif - UINT32 average = accum / accum_clocks; + unsigned int average = accum / accum_clocks; assert(average <= 15); // above this would indicate overflow return average; } // Tick the DMC for the number of clocks, and return output counter; - UINT32 NES_DMC::calc_dmc (UINT32 clocks) + unsigned int NES_DMC::calc_dmc (unsigned int clocks) { counter[2] -= clocks; assert (dfreq > 0); // prevent infinite loop @@ -300,8 +260,7 @@ namespace xgm { if (dlength > 0) { - memory->Read (daddress, data); - cpu->StealCycles(4); // DMC read takes 3 or 4 CPU cycles, usually 4 + memory (daddress, data); // (checking for the 3-cycle case would require sub-instruction emulation) data &= 0xFF; // read 8 bits if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data]; @@ -319,7 +278,6 @@ namespace xgm else if (mode & 2) // IRQ and not looped { irq = true; - cpu->UpdateIRQ(NES_CPU::IRQD_DMC, true); } } } @@ -334,7 +292,7 @@ namespace xgm return (damp<<1) + dac_lsb; } - void NES_DMC::TickFrameSequence (UINT32 clocks) + void NES_DMC::TickFrameSequence (unsigned int clocks) { frame_sequence_count += clocks; while (frame_sequence_count > frame_sequence_length) @@ -347,28 +305,28 @@ namespace xgm } } - void NES_DMC::Tick (UINT32 clocks) + void NES_DMC::Tick (unsigned int clocks) { out[0] = calc_tri(clocks); out[1] = calc_noise(clocks); out[2] = calc_dmc(clocks); } - UINT32 NES_DMC::Render (INT32 b[2]) + unsigned int NES_DMC::Render (int b[2]) { out[0] = (mask & 1) ? 0 : out[0]; out[1] = (mask & 2) ? 0 : out[1]; out[2] = (mask & 4) ? 0 : out[2]; - INT32 m[3]; + int m[3]; m[0] = tnd_table[0][out[0]][0][0]; m[1] = tnd_table[0][0][out[1]][0]; m[2] = tnd_table[0][0][0][out[2]]; if (option[OPT_NONLINEAR_MIXER]) { - INT32 ref = m[0] + m[1] + m[2]; - INT32 voltage = tnd_table[1][out[0]][out[1]][out[2]]; + int ref = m[0] + m[1] + m[2]; + int voltage = tnd_table[1][out[0]][out[1]][out[2]]; if (ref) { for (int i=0; i < 3; ++i) @@ -391,7 +349,7 @@ namespace xgm dmc_pop = false; // prevent overflow, keep headspace at edges - const INT32 OFFSET_MAX = (1 << 30) - (4 << 16); + const int OFFSET_MAX = (1 << 30) - (4 << 16); if (dmc_pop_offset > OFFSET_MAX) dmc_pop_offset = OFFSET_MAX; if (dmc_pop_offset < -OFFSET_MAX) dmc_pop_offset = -OFFSET_MAX; } @@ -425,7 +383,7 @@ namespace xgm void NES_DMC::SetRate (double r) { - rate = (UINT32)(r?r:DEFAULT_RATE); + rate = (unsigned int)(r?r:DEFAULT_RATE); } void NES_DMC::SetPal (bool is_pal) @@ -455,7 +413,7 @@ namespace xgm for(int t=0; t<16 ; t++) { for(int n=0; n<16; n++) { for(int d=0; d<128; d++) { - tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0); + tnd_table[0][t][n][d] = (unsigned int)(MASTER*(3.0*t+2.0*n+d)/208.0); } } } @@ -466,7 +424,7 @@ namespace xgm for(int n=0; n<16; n++) { for(int d=0; d<128; d++) { if(t!=0||n!=0||d!=0) - tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); + tnd_table[1][t][n][d] = (unsigned int)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); } } } @@ -510,7 +468,6 @@ namespace xgm frame_sequence_count = 0; frame_sequence_steps = 4; frame_sequence_step = 0; - cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); for (i = 0; i < 0x0F; i++) Write (0x4008 + i, 0); @@ -520,7 +477,6 @@ namespace xgm Write (0x4015, 0x00); if (option[OPT_UNMUTE_ON_RESET]) Write (0x4015, 0x0f); - cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); out[0] = out[1] = out[2] = 0; damp = 0; @@ -551,7 +507,7 @@ namespace xgm SetRate(rate); } - void NES_DMC::SetMemory (IDevice * r) + void NES_DMC::SetMemory (std::function r) { memory = r; } @@ -566,9 +522,9 @@ namespace xgm } } - bool NES_DMC::Write (UINT32 adr, UINT32 val, UINT32 id) + bool NES_DMC::Write (unsigned int adr, unsigned int val, unsigned int id) { - static const UINT8 length_table[32] = { + static const unsigned char length_table[32] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, @@ -612,7 +568,6 @@ namespace xgm } irq = false; - cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); reg[adr-0x4008] = val; return true; @@ -623,7 +578,6 @@ namespace xgm //DEBUG_OUT("4017 = %02XÂ¥n", val); frame_irq_enable = ((val & 0x40) != 0x40); if (frame_irq_enable) frame_irq = false; - cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); frame_sequence_count = 0; if (val & 0x80) @@ -708,7 +662,6 @@ namespace xgm if (!(mode & 2)) { irq = false; - cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); } dfreq = freq_table[pal][val&15]; break; @@ -739,7 +692,7 @@ namespace xgm return true; } - bool NES_DMC::Read (UINT32 adr, UINT32 & val, UINT32 id) + bool NES_DMC::Read (unsigned int adr, unsigned int & val, unsigned int id) { if (adr == 0x4015) { @@ -751,7 +704,6 @@ namespace xgm ; frame_irq = false; - cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); return true; } else if (0x4008<=adr&&adr<=0x4014) @@ -762,10 +714,4 @@ namespace xgm else return false; } - - // IRQ support requires CPU read access - void NES_DMC::SetCPU(NES_CPU* cpu_) - { - cpu = cpu_; - } } // namespace diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h index 6cc8058a2..4d19617d8 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h @@ -1,16 +1,13 @@ #ifndef _NES_DMC_H_ #define _NES_DMC_H_ - -#include "../device.h" -#include "../Audio/MedianFilter.h" -#include "../CPU/nes_cpu.h" +#include namespace xgm { class NES_APU; // forward declaration /** Bottom Half of APU **/ - class NES_DMC:public ISoundChip + class NES_DMC { public: enum @@ -28,46 +25,46 @@ namespace xgm }; protected: const int GETA_BITS; - static const UINT32 freq_table[2][16]; - static const UINT32 wavlen_table[2][16]; - UINT32 tnd_table[2][16][16][128]; + static const unsigned int freq_table[2][16]; + static const unsigned int wavlen_table[2][16]; + unsigned int tnd_table[2][16][16][128]; int option[OPT_END]; int mask; - INT32 sm[2][3]; - UINT8 reg[0x10]; - UINT32 len_reg; - UINT32 adr_reg; - IDevice *memory; - UINT32 out[3]; - UINT32 daddress; - UINT32 dlength; - UINT32 data; + int sm[2][3]; + unsigned int reg[0x10]; + unsigned int len_reg; + unsigned int adr_reg; + std::function memory; + unsigned int out[3]; + unsigned int daddress; + unsigned int dlength; + unsigned int data; bool empty; - INT16 damp; + short damp; int dac_lsb; bool dmc_pop; - INT32 dmc_pop_offset; - INT32 dmc_pop_follow; + int dmc_pop_offset; + int dmc_pop_follow; double clock; - UINT32 rate; + unsigned int rate; int pal; int mode; bool irq; - INT32 counter[3]; // frequency dividers + int counter[3]; // frequency dividers int tphase; // triangle phase - UINT32 nfreq; // noise frequency - UINT32 dfreq; // DPCM frequency + unsigned int nfreq; // noise frequency + unsigned int dfreq; // DPCM frequency - UINT32 tri_freq; + unsigned int tri_freq; int linear_counter; int linear_counter_reload; bool linear_counter_halt; bool linear_counter_control; int noise_volume; - UINT32 noise, noise_tap; + unsigned int noise, noise_tap; // noise envelope bool envelope_loop; @@ -80,8 +77,6 @@ namespace xgm bool enable[2]; // tri/noise enable int length_counter[2]; // 0=tri, 1=noise - TrackInfoBasic trkinfo[3]; - // frame sequencer NES_APU* apu; // apu is clocked by DMC's frame sequencer int frame_sequence_count; // current cycle count @@ -91,37 +86,32 @@ namespace xgm bool frame_irq; bool frame_irq_enable; - NES_CPU* cpu; // IRQ needs CPU access - - inline UINT32 calc_tri (UINT32 clocks); - inline UINT32 calc_dmc (UINT32 clocks); - inline UINT32 calc_noise (UINT32 clocks); + inline unsigned int calc_tri (unsigned int clocks); + inline unsigned int calc_dmc (unsigned int clocks); + inline unsigned int calc_noise (unsigned int clocks); public: NES_DMC (); - ‾NES_DMC (); + ~NES_DMC (); void InitializeTNDTable(double wt, double wn, double wd); void SetPal (bool is_pal); void SetAPU (NES_APU* apu_); - void SetMemory (IDevice * r); + void SetMemory (std::function r); void FrameSequence(int s); int GetDamp(){ return (damp<<1)|dac_lsb ; } - void TickFrameSequence (UINT32 clocks); + void TickFrameSequence (unsigned int clocks); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + 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, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - - void SetCPU(NES_CPU* cpu_); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp index 1e08100c0..f3e61decc 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp @@ -1,5 +1,7 @@ #include +#include #include "nes_fds.h" +#include "common.h" namespace xgm { @@ -22,11 +24,11 @@ NES_FDS::NES_FDS () Reset(); } -NES_FDS::‾NES_FDS () +NES_FDS::~NES_FDS () { } -void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 mixr) +void NES_FDS::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 1) return; @@ -34,20 +36,6 @@ void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 mixr) sm[1] = mixr; } -ITrackInfo *NES_FDS::GetTrackInfo(int trk) -{ - trkinfo.max_volume = 32; - trkinfo.volume = last_vol; - trkinfo.key = last_vol > 0; - trkinfo._freq = last_freq; - trkinfo.freq = (double(last_freq) * clock) / (65536.0 * 64.0); - trkinfo.tone = env_out[EMOD]; - for(int i=0;i<64;i++) - trkinfo.wave[i] = wave[TWAV][i]; - - return &trkinfo; -} - void NES_FDS::SetClock (double c) { clock = c; @@ -61,8 +49,8 @@ void NES_FDS::SetRate (double r) double cutoff = double(option[OPT_CUTOFF]); double leak = 0.0; if (cutoff > 0) - leak = ::exp(-2.0 * 3.14159 * cutoff / rate); - rc_k = INT32(leak * double(1<= period) { // clock the envelope @@ -158,22 +146,22 @@ void NES_FDS::Tick (UINT32 clocks) if (!mod_halt) { // advance phase, adjust for modulator - UINT32 start_pos = phase[TMOD] >> 16; + unsigned int start_pos = phase[TMOD] >> 16; phase[TMOD] += (clocks * freq[TMOD]); - UINT32 end_pos = phase[TMOD] >> 16; + unsigned int end_pos = phase[TMOD] >> 16; // wrap the phase to the 64-step table (+ 16 bit accumulator) phase[TMOD] = phase[TMOD] & 0x3FFFFF; // execute all clocked steps - for (UINT32 p = start_pos; p < end_pos; ++p) + for (unsigned int p = start_pos; p < end_pos; ++p) { - INT32 wv = wave[TMOD][p & 0x3F]; + int wv = wave[TMOD][p & 0x3F]; if (wv == 4) // 4 resets mod position mod_pos = 0; else { - const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; + const int BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; mod_pos += BIAS[wv]; mod_pos &= 0x7F; // 7-bit clamp } @@ -184,16 +172,16 @@ void NES_FDS::Tick (UINT32 clocks) if (!wav_halt) { // complex mod calculation - INT32 mod = 0; + int mod = 0; if (env_out[EMOD] != 0) // skip if modulator off { // convert mod_pos to 7-bit signed - INT32 pos = (mod_pos < 64) ? mod_pos : (mod_pos-128); + int pos = (mod_pos < 64) ? mod_pos : (mod_pos-128); // multiply pos by gain, // shift off 4 bits but with odd "rounding" behaviour - INT32 temp = pos * env_out[EMOD]; - INT32 rem = temp & 0x0F; + int temp = pos * env_out[EMOD]; + int rem = temp & 0x0F; temp >>= 4; if ((rem > 0) && ((temp & 0x80) == 0)) { @@ -216,7 +204,7 @@ void NES_FDS::Tick (UINT32 clocks) } // advance wavetable position - INT32 f = freq[TWAV] + mod; + int f = freq[TWAV] + mod; phase[TWAV] = phase[TWAV] + (clocks * f); phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap @@ -225,7 +213,7 @@ void NES_FDS::Tick (UINT32 clocks) } // output volume caps at 32 - INT32 vol_out = env_out[EVOL]; + int vol_out = env_out[EVOL]; if (vol_out > 32) vol_out = 32; // final output @@ -242,32 +230,32 @@ void NES_FDS::Tick (UINT32 clocks) last_vol = vol_out; } -UINT32 NES_FDS::Render (INT32 b[2]) +unsigned int NES_FDS::Render (int b[2]) { // 8 bit approximation of master volume const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223) const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol - const INT32 MASTER[4] = { + const int MASTER[4] = { int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) }; - INT32 v = fout * MASTER[master_vol] >> 8; + int v = fout * MASTER[master_vol] >> 8; // lowpass RC filter - INT32 rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS; + int rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS; rc_accum = rc_out; v = rc_out; // output mix - INT32 m = mask ? 0 : v; + int m = mask ? 0 : v; b[0] = (m * sm[0]) >> 7; b[1] = (m * sm[1]) >> 7; return 2; } -bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id) +bool NES_FDS::Write (unsigned int adr, unsigned int val, unsigned int id) { // $4023 master I/O enable/disable if (adr == 0x4023) @@ -368,7 +356,7 @@ bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id) return false; } -bool NES_FDS::Read (UINT32 adr, UINT32 & val, UINT32 id) +bool NES_FDS::Read (unsigned int adr, unsigned int & val, unsigned int id) { if (adr >= 0x4040 && adr <= 0x407F) { diff --git a/src/engine/platform/sound/nes_nsfplay/nes_fds.h b/src/engine/platform/sound/nes_nsfplay/nes_fds.h index 67c60fd1c..180841dd8 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_fds.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_fds.h @@ -1,17 +1,9 @@ #ifndef _NES_FDS_H_ #define _NES_FDS_H_ -#include "../device.h" namespace xgm { -class TrackInfoFDS : public TrackInfoBasic -{ -public: - INT16 wave[64]; - virtual IDeviceInfo *Clone(){ return new TrackInfoFDS(*this); } -}; - -class NES_FDS : public ISoundChip +class NES_FDS { public: enum @@ -25,57 +17,55 @@ public: protected: double rate, clock; int mask; - INT32 sm[2]; // stereo mix - INT32 fout; // current output - TrackInfoFDS trkinfo; + int sm[2]; // stereo mix + int fout; // current output int option[OPT_END]; bool master_io; - UINT32 master_vol; - UINT32 last_freq; // for trackinfo - UINT32 last_vol; // for trackinfo + unsigned int master_vol; + unsigned int last_freq; // for trackinfo + unsigned int last_vol; // for trackinfo // two wavetables enum { TMOD=0, TWAV=1 }; - INT32 wave[2][64]; - UINT32 freq[2]; - UINT32 phase[2]; + int wave[2][64]; + unsigned int freq[2]; + unsigned int phase[2]; bool wav_write; bool wav_halt; bool env_halt; bool mod_halt; - UINT32 mod_pos; - UINT32 mod_write_pos; + unsigned int mod_pos; + unsigned int mod_write_pos; // two ramp envelopes enum { EMOD=0, EVOL=1 }; bool env_mode[2]; bool env_disable[2]; - UINT32 env_timer[2]; - UINT32 env_speed[2]; - UINT32 env_out[2]; - UINT32 master_env_speed; + unsigned int env_timer[2]; + unsigned int env_speed[2]; + unsigned int env_out[2]; + unsigned int master_env_speed; // 1-pole RC lowpass filter - INT32 rc_accum; - INT32 rc_k; - INT32 rc_l; + int rc_accum; + int rc_k; + int rc_l; public: NES_FDS (); - virtual ‾ NES_FDS (); + ~NES_FDS (); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + 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, INT16 mixl, INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace xgm diff --git a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp index 9243f6595..500be87ca 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp @@ -1,11 +1,11 @@ #include "nes_mmc5.h" +#include "common.h" namespace xgm { NES_MMC5::NES_MMC5 () { - cpu = NULL; SetClock (DEFAULT_CLOCK); SetRate (DEFAULT_RATE); option[OPT_NONLINEAR_MIXER] = true; @@ -15,19 +15,19 @@ namespace xgm // square nonlinear mix, same as 2A03 square_table[0] = 0; for(int i=1;i<32;i++) - square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + square_table[i]=(int)((8192.0*95.88)/(8128.0/i+100)); // 2A03 style nonlinear pcm mix with double the bits //pcm_table[0] = 0; - //INT32 wd = 22638; + //int wd = 22638; //for(int d=1;d<256; ++d) - // pcm_table[d] = (INT32)((8192.0*159.79)/(100.0+1.0/((double)d/wd))); + // pcm_table[d] = (int)((8192.0*159.79)/(100.0+1.0/((double)d/wd))); // linear pcm mix (actual hardware seems closer to this) pcm_table[0] = 0; double pcm_scale = 32.0; for (int d=1; d<256; ++d) - pcm_table[d] = (INT32)(double(d) * pcm_scale); + pcm_table[d] = (int)(double(d) * pcm_scale); // stereo mix for(int c=0;c<2;++c) @@ -35,7 +35,7 @@ namespace xgm sm[c][t] = 128; } - NES_MMC5::‾NES_MMC5 () + NES_MMC5::~NES_MMC5 () { } @@ -124,9 +124,9 @@ namespace xgm } } - INT32 NES_MMC5::calc_sqr (int i, UINT32 clocks) + int NES_MMC5::calc_sqr (int i, unsigned int clocks) { - static const INT16 sqrtbl[4][16] = { + static const short sqrtbl[4][16] = { {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, @@ -140,7 +140,7 @@ namespace xgm scounter[i] -= (freq[i] + 1); } - INT32 ret = 0; + int ret = 0; if (length_counter[i] > 0) { // note MMC5 does not silence the highest 8 frequencies like APU, @@ -153,7 +153,7 @@ namespace xgm return ret; } - void NES_MMC5::TickFrameSequence (UINT32 clocks) + void NES_MMC5::TickFrameSequence (unsigned int clocks) { frame_sequence_count += clocks; while (frame_sequence_count > 7458) @@ -163,28 +163,28 @@ namespace xgm } } - void NES_MMC5::Tick (UINT32 clocks) + void NES_MMC5::Tick (unsigned int clocks) { out[0] = calc_sqr(0, clocks); out[1] = calc_sqr(1, clocks); out[2] = pcm; } - UINT32 NES_MMC5::Render (INT32 b[2]) + unsigned int NES_MMC5::Render (int b[2]) { out[0] = (mask & 1) ? 0 : out[0]; out[1] = (mask & 2) ? 0 : out[1]; out[2] = (mask & 4) ? 0 : out[2]; - INT32 m[3]; + int m[3]; if(option[OPT_NONLINEAR_MIXER]) { // squares nonlinear - INT32 voltage = square_table[out[0] + out[1]]; + int voltage = square_table[out[0] + out[1]]; m[0] = out[0] << 6; m[1] = out[1] << 6; - INT32 ref = m[0] + m[1]; + int ref = m[0] + m[1]; if (ref > 0) { m[0] = (m[0] * voltage) / ref; @@ -224,11 +224,11 @@ namespace xgm return 2; } - bool NES_MMC5::Write (UINT32 adr, UINT32 val, UINT32 id) + bool NES_MMC5::Write (unsigned int adr, unsigned int val, unsigned int id) { int ch; - static const UINT8 length_table[32] = { + static const unsigned char length_table[32] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, @@ -329,19 +329,8 @@ namespace xgm return true; } - bool NES_MMC5::Read (UINT32 adr, UINT32 & val, UINT32 id) + bool NES_MMC5::Read (unsigned int adr, unsigned int & val, unsigned int id) { - // in PCM read mode, reads from $8000-$C000 automatically load the PCM output - if (pcm_mode && (0x8000 <= adr) && (adr < 0xC000) && cpu) - { - pcm_mode = false; // prevent recursive entry - UINT32 pcm_read; - cpu->Read(adr, pcm_read); - pcm_read &= 0xFF; - if (pcm_read != 0) - pcm = pcm_read; - pcm_mode = true; - } if ((0x5000 <= adr) && (adr < 0x5008)) { @@ -373,50 +362,11 @@ namespace xgm return false; } - void NES_MMC5::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + void NES_MMC5::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 2) return; sm[0][trk] = mixl; sm[1][trk] = mixr; } - - ITrackInfo *NES_MMC5::GetTrackInfo(int trk) - { - assert(trk<3); - - if (trk < 2) // square - { - trkinfo[trk]._freq = freq[trk]; - if(freq[trk]) - trkinfo[trk].freq = clock/16/(freq[trk] + 1); - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = out[trk]; - trkinfo[trk].max_volume = 15; - trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10); - trkinfo[trk].key = (envelope_disable[trk]?(volume[trk]>0): (envelope_counter[trk]>0)); - trkinfo[trk].tone = duty[trk]; - } - else // pcm - { - trkinfo[trk]._freq = 0; - trkinfo[trk].freq = 0; - trkinfo[trk].output = out[2]; - trkinfo[trk].max_volume = 255; - trkinfo[trk].volume = pcm; - trkinfo[trk].key = 0; - trkinfo[trk].tone = pcm_mode ? 1 : 0; - } - - return &trkinfo[trk]; - } - - // pcm read mode requires CPU read access - void NES_MMC5::SetCPU(NES_CPU* cpu_) - { - cpu = cpu_; - } - }// namespace diff --git a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h index 5747ab0d6..d0ae8bd0c 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h @@ -1,11 +1,9 @@ #ifndef _NES_MMC5_H_ #define _NES_MMC5_H_ -#include "../device.h" -#include "../CPU/nes_cpu.h" namespace xgm { - class NES_MMC5:public ISoundChip + class NES_MMC5 { public: enum @@ -14,21 +12,20 @@ namespace xgm protected: int option[OPT_END]; int mask; - INT32 sm[2][3]; // stereo panning - UINT8 ram[0x6000 - 0x5c00]; - UINT8 reg[8]; - UINT8 mreg[2]; - UINT8 pcm; // PCM channel + int sm[2][3]; // stereo panning + unsigned char ram[0x6000 - 0x5c00]; + unsigned char reg[8]; + unsigned char mreg[2]; + unsigned char pcm; // PCM channel bool pcm_mode; // PCM channel - NES_CPU* cpu; // PCM channel reads need CPU access - UINT32 scounter[2]; // frequency divider - UINT32 sphase[2]; // phase counter + unsigned int scounter[2]; // frequency divider + unsigned int sphase[2]; // phase counter - UINT32 duty[2]; - UINT32 volume[2]; - UINT32 freq[2]; - INT32 out[3]; + unsigned int duty[2]; + unsigned int volume[2]; + unsigned int freq[2]; + int out[3]; bool enable[2]; bool envelope_disable[2]; // エンベロープ有効フラグ @@ -43,30 +40,26 @@ namespace xgm int frame_sequence_count; double clock, rate; - INT32 calc_sqr (int i, UINT32 clocks); - INT32 square_table[32]; - INT32 pcm_table[256]; - TrackInfoBasic trkinfo[3]; + int calc_sqr (int i, unsigned int clocks); + int square_table[32]; + int pcm_table[256]; public: NES_MMC5 (); - ‾NES_MMC5 (); + ~NES_MMC5 (); void FrameSequence (); - void TickFrameSequence (UINT32 clocks); + void TickFrameSequence (unsigned int clocks); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + 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, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - - void SetCPU(NES_CPU* cpu_); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp b/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp index ae793dc08..25e80fb47 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp @@ -1,5 +1,6 @@ #include #include "nes_n106.h" +#include "common.h" namespace xgm { @@ -18,11 +19,11 @@ NES_N106::NES_N106 () Reset(); } -NES_N106::‾NES_N106 () +NES_N106::~NES_N106 () { } -void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr) +void NES_N106::SetStereoMix (int trk, short mixl, short mixr) { if (trk < 0 || trk >= 8) return; trk = 7-trk; // displayed channels are inverted @@ -30,42 +31,6 @@ void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr) sm[1][trk] = mixr; } -ITrackInfo *NES_N106::GetTrackInfo (int trk) -{ - int channels = get_channels(); - int channel = 7-trk; // invert the track display - - TrackInfoN106* t = &trkinfo[channel]; - - if (trk >= channels) - { - t->max_volume = 15; - t->volume = 0; - t->_freq = 0; - t->wavelen = 0; - t->tone = -1; - t->output = 0; - t->key = false; - t->freq = 0; - } - else - { - t->max_volume = 15; - t->volume = get_vol(channel); - t->_freq = get_freq(channel); - t->wavelen = get_len(channel); - t->tone = get_off(channel); - t->output = fout[channel]; - - t->key = (t->volume > 0) && (t->_freq > 0); - t->freq = (double(t->_freq) * clock) / double(15 * 65536 * channels * t->wavelen); - - for (int i=0; i < t->wavelen; ++i) - t->wave[i] = get_sample((i+t->tone)&0xFF); - } - - return t; -} void NES_N106::SetClock (double c) { @@ -120,7 +85,7 @@ void NES_N106::Reset () Write(0xF800, 0x00); // select $00 without auto-increment } -void NES_N106::Tick (UINT32 clocks) +void NES_N106::Tick (unsigned int clocks) { if (master_disable) return; @@ -132,24 +97,24 @@ void NES_N106::Tick (UINT32 clocks) { int channel = 7-tick_channel; - UINT32 phase = get_phase(channel); - UINT32 freq = get_freq(channel); - UINT32 len = get_len(channel); - UINT32 off = get_off(channel); - INT32 vol = get_vol(channel); + unsigned int phase = get_phase(channel); + unsigned int freq = get_freq(channel); + unsigned int len = get_len(channel); + unsigned int off = get_off(channel); + int vol = get_vol(channel); // accumulate 24-bit phase phase = (phase + freq) & 0x00FFFFFF; // wrap phase if wavelength exceeded - UINT32 hilen = len << 16; + unsigned int hilen = len << 16; while (phase >= hilen) phase -= hilen; // write back phase set_phase(phase, channel); // fetch sample (note: N163 output is centred at 8, and inverted w.r.t 2A03) - INT32 sample = 8 - get_sample(((phase >> 16) + off) & 0xFF); + int sample = 8 - get_sample(((phase >> 16) + off) & 0xFF); fout[channel] = sample * vol; // cycle to next channel every 15 clocks @@ -160,7 +125,7 @@ void NES_N106::Tick (UINT32 clocks) } } -UINT32 NES_N106::Render (INT32 b[2]) +unsigned int NES_N106::Render (int b[2]) { b[0] = 0; b[1] = 0; @@ -217,7 +182,7 @@ UINT32 NES_N106::Render (INT32 b[2]) } // mix together, increase output level by 8 bits, roll off 7 bits from sm - INT32 MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 }; + int MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 }; b[0] = (b[0] * MIX[channels]) >> 7; b[1] = (b[1] * MIX[channels]) >> 7; // when approximating the serial multiplex as a straight mix, once the @@ -233,14 +198,14 @@ UINT32 NES_N106::Render (INT32 b[2]) // and lower volumes on others. Using 6.0x as a rough "one size fits all". const double MASTER_VOL = 6.0 * 1223.0; const double MAX_OUT = 15.0 * 15.0 * 256.0; // max digital value - const INT32 GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f); + const int GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f); b[0] = (b[0] * GAIN) >> 8; b[1] = (b[1] * GAIN) >> 8; return 2; } -bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id) +bool NES_N106::Write (unsigned int adr, unsigned int val, unsigned int id) { if (adr == 0xE000) // master disable { @@ -286,7 +251,7 @@ bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id) return false; } -bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id) +bool NES_N106::Read (unsigned int adr, unsigned int & val, unsigned int id) { if (adr == 0x4800) // register read { @@ -302,7 +267,7 @@ bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id) // register decoding/encoding functions // -inline UINT32 NES_N106::get_phase (int channel) +inline unsigned int NES_N106::get_phase (int channel) { // 24-bit phase stored in channel regs 1/3/5 channel = channel << 3; @@ -311,7 +276,7 @@ inline UINT32 NES_N106::get_phase (int channel) + (reg[0x45 + channel] << 16); } -inline UINT32 NES_N106::get_freq (int channel) +inline unsigned int NES_N106::get_freq (int channel) { // 19-bit frequency stored in channel regs 0/2/4 channel = channel << 3; @@ -320,28 +285,28 @@ inline UINT32 NES_N106::get_freq (int channel) + ((reg[0x44 + channel] & 0x03) << 16); } -inline UINT32 NES_N106::get_off (int channel) +inline unsigned int NES_N106::get_off (int channel) { // 8-bit offset stored in channel reg 6 channel = channel << 3; return reg[0x46 + channel]; } -inline UINT32 NES_N106::get_len (int channel) +inline unsigned int NES_N106::get_len (int channel) { // 6-bit<<3 length stored obscurely in channel reg 4 channel = channel << 3; return 256 - (reg[0x44 + channel] & 0xFC); } -inline INT32 NES_N106::get_vol (int channel) +inline int NES_N106::get_vol (int channel) { // 4-bit volume stored in channel reg 7 channel = channel << 3; return reg[0x47 + channel] & 0x0F; } -inline INT32 NES_N106::get_sample (UINT32 index) +inline int NES_N106::get_sample (unsigned int index) { // every sample becomes 2 samples in regs return (index&1) ? @@ -355,7 +320,7 @@ inline int NES_N106::get_channels () return ((reg[0x7F] >> 4) & 0x07) + 1; } -inline void NES_N106::set_phase (UINT32 phase, int channel) +inline void NES_N106::set_phase (unsigned int phase, int channel) { // 24-bit phase stored in channel regs 1/3/5 channel = channel << 3; diff --git a/src/engine/platform/sound/nes_nsfplay/nes_n106.h b/src/engine/platform/sound/nes_nsfplay/nes_n106.h index aebb6cb46..5ce937e26 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_n106.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_n106.h @@ -1,18 +1,10 @@ #ifndef _NES_N106_H_ #define _NES_N106_H_ -#include "../device.h" namespace xgm { -class TrackInfoN106 : public TrackInfoBasic -{ -public: - int wavelen; - INT16 wave[256]; - virtual IDeviceInfo *Clone(){ return new TrackInfoN106(*this); } -}; -class NES_N106:public ISoundChip +class NES_N106 { public: enum @@ -26,13 +18,12 @@ public: protected: double rate, clock; int mask; - INT32 sm[2][8]; // stereo mix - INT32 fout[8]; // current output - TrackInfoN106 trkinfo[8]; + int sm[2][8]; // stereo mix + int fout[8]; // current output int option[OPT_END]; bool master_disable; - UINT32 reg[0x80]; // all state is contained here + unsigned int reg[0x80]; // all state is contained here unsigned int reg_select; bool reg_advance; int tick_channel; @@ -42,31 +33,30 @@ protected: int render_subclock; // convenience functions to interact with regs - inline UINT32 get_phase (int channel); - inline UINT32 get_freq (int channel); - inline UINT32 get_off (int channel); - inline UINT32 get_len (int channel); - inline INT32 get_vol (int channel); - inline INT32 get_sample (UINT32 index); + inline unsigned int get_phase (int channel); + inline unsigned int get_freq (int channel); + inline unsigned int get_off (int channel); + inline unsigned int get_len (int channel); + inline int get_vol (int channel); + inline int get_sample (unsigned int index); inline int get_channels (); // for storing back the phase after modifying - inline void set_phase (UINT32 phase, int channel); + inline void set_phase (unsigned int phase, int channel); public: NES_N106 (); - ‾NES_N106 (); + ~NES_N106 (); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + 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, INT16 mixl, INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace xgm diff --git a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp index c46109403..754d44cf7 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp @@ -1,4 +1,5 @@ #include "nes_vrc6.h" +#include "common.h" namespace xgm { @@ -16,11 +17,11 @@ namespace xgm sm[c][t] = 128; } - NES_VRC6::‾NES_VRC6 () + NES_VRC6::~NES_VRC6 () { } - void NES_VRC6::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + void NES_VRC6::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 2) return; @@ -28,32 +29,6 @@ namespace xgm sm[1][trk] = mixr; } - ITrackInfo *NES_VRC6::GetTrackInfo(int trk) - { - if(trk<2) - { - trkinfo[trk].max_volume = 15; - trkinfo[trk].volume = volume[trk]; - trkinfo[trk]._freq = freq2[trk]; - trkinfo[trk].freq = freq2[trk]?clock/16/(freq2[trk]+1):0; - trkinfo[trk].tone = duty[trk]; - trkinfo[trk].key = (volume[trk]>0)&&enable[trk]&&!gate[trk]; - return &trkinfo[trk]; - } - else if(trk==2) - { - trkinfo[2].max_volume = 255; - trkinfo[2].volume = volume[2]; - trkinfo[2]._freq = freq2[2]; - trkinfo[2].freq = freq2[2]?clock/14/(freq2[2]+1):0; - trkinfo[2].tone = -1; - trkinfo[2].key = (enable[2]>0); - return &trkinfo[2]; - } - else - return NULL; - } - void NES_VRC6::SetClock (double c) { clock = c; @@ -91,9 +66,9 @@ namespace xgm phase[0] = 2; } - INT16 NES_VRC6::calc_sqr (int i, UINT32 clocks) + short NES_VRC6::calc_sqr (int i, unsigned int clocks) { - static const INT16 sqrtbl[8][16] = { + static const short sqrtbl[8][16] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}, @@ -121,7 +96,7 @@ namespace xgm || sqrtbl[duty[i]][phase[i]])? volume[i] : 0; } - INT16 NES_VRC6::calc_saw (UINT32 clocks) + short NES_VRC6::calc_saw (unsigned int clocks) { if (!enable[2]) return 0; @@ -151,16 +126,16 @@ namespace xgm return phase[2] >> 3; } - void NES_VRC6::Tick (UINT32 clocks) + void NES_VRC6::Tick (unsigned int clocks) { out[0] = calc_sqr(0,clocks); out[1] = calc_sqr(1,clocks); out[2] = calc_saw(clocks); } - UINT32 NES_VRC6::Render (INT32 b[2]) + unsigned int NES_VRC6::Render (int b[2]) { - INT32 m[3]; + int m[3]; m[0] = out[0]; m[1] = out[1]; m[2] = out[2]; @@ -182,14 +157,14 @@ namespace xgm //b[1] >>= (7 - 7); // master volume adjustment - const INT32 MASTER = INT32(256.0 * 1223.0 / 1920.0); + const int MASTER = int(256.0 * 1223.0 / 1920.0); b[0] = (b[0] * MASTER) >> 8; b[1] = (b[1] * MASTER) >> 8; return 2; } - bool NES_VRC6::Write (UINT32 adr, UINT32 val, UINT32 id) + bool NES_VRC6::Write (unsigned int adr, unsigned int val, unsigned int id) { int ch, cmap[4] = { 0, 0, 1, 2 }; @@ -255,7 +230,7 @@ namespace xgm return true; } - bool NES_VRC6::Read (UINT32 adr, UINT32 & val, UINT32 id) + bool NES_VRC6::Read (unsigned int adr, unsigned int & val, unsigned int id) { return false; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h index 2bb64eb8a..85fed39c4 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h @@ -1,11 +1,10 @@ #ifndef _NES_VRC6_H_ #define _NES_VRC6_H_ -#include "../device.h" namespace xgm { - class NES_VRC6:public ISoundChip + class NES_VRC6 { public: enum @@ -13,42 +12,40 @@ namespace xgm OPT_END }; protected: - UINT32 counter[3]; // frequency divider - UINT32 phase[3]; // phase counter - UINT32 freq2[3]; // adjusted frequency + unsigned int counter[3]; // frequency divider + unsigned int phase[3]; // phase counter + unsigned int freq2[3]; // adjusted frequency int count14; // saw 14-stage counter //int option[OPT_END]; int mask; - INT32 sm[2][3]; // stereo mix + int sm[2][3]; // stereo mix int duty[2]; int volume[3]; int enable[3]; int gate[3]; - UINT32 freq[3]; - INT16 calc_sqr (int i, UINT32 clocks); - INT16 calc_saw (UINT32 clocks); + unsigned int freq[3]; + short calc_sqr (int i, unsigned int clocks); + short calc_saw (unsigned int clocks); bool halt; int freq_shift; double clock, rate; - INT32 out[3]; - TrackInfoBasic trkinfo[3]; + int out[3]; public: NES_VRC6 (); - ‾NES_VRC6 (); + ~NES_VRC6 (); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + 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, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace From cb7aa4aa058fdf423a2eb7094f1db26ed47df378 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:20:20 -0500 Subject: [PATCH 11/26] NES: wire up NSFplay no muting supported for now see #27 --- src/engine/dispatchContainer.cpp | 1 + src/engine/platform/nes.cpp | 147 +++++++++++++----- src/engine/platform/nes.h | 10 ++ .../platform/sound/nes_nsfplay/nes_apu.h | 20 +-- .../platform/sound/nes_nsfplay/nes_dmc.h | 20 +-- .../platform/sound/nes_nsfplay/nes_fds.h | 20 +-- .../platform/sound/nes_nsfplay/nes_mmc5.h | 20 +-- .../platform/sound/nes_nsfplay/nes_n106.h | 20 +-- .../platform/sound/nes_nsfplay/nes_vrc6.h | 20 +-- src/gui/gui.h | 2 + src/gui/settings.cpp | 12 ++ 11 files changed, 193 insertions(+), 99 deletions(-) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index e9f1a03e2..187523c72 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -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; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 7f35dc5bd..084c1123c 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -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=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=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=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; iTick(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& 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() { diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index abd23da99..8b898c7fc 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -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); diff --git a/src/engine/platform/sound/nes_nsfplay/nes_apu.h b/src/engine/platform/sound/nes_nsfplay/nes_apu.h index 004c99974..b302dd02c 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_apu.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_apu.h @@ -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 diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h index 4d19617d8..b0aa2ef92 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h @@ -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); }; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_fds.h b/src/engine/platform/sound/nes_nsfplay/nes_fds.h index 180841dd8..9185e4da6 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_fds.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_fds.h @@ -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 diff --git a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h index d0ae8bd0c..19d5ab269 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h @@ -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); }; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_n106.h b/src/engine/platform/sound/nes_nsfplay/nes_n106.h index 5ce937e26..a59dd6687 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_n106.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_n106.h @@ -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 diff --git a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h index 85fed39c4..198a8bd66 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h @@ -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 diff --git a/src/gui/gui.h b/src/gui/gui.h index 520abdc41..49e56f0c2 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -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), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 152f94b8c..383fa4202 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -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); From 9479a8aa0ff148d7a44d51b5e3534f99c6f83566 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:29:00 -0500 Subject: [PATCH 12/26] fix typo in playback engine possible memory leak --- src/engine/playback.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index cb4d663d1..48ffd08a0 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1847,8 +1847,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi } runtotal[i]=blip_clocks_needed(disCont[i].bb[0],size-lastAvail[i]); if (runtotal[i]>disCont[i].bbInLen) { - delete disCont[i].bbIn[0]; - delete disCont[i].bbIn[1]; + delete[] disCont[i].bbIn[0]; + delete[] disCont[i].bbIn[1]; disCont[i].bbIn[0]=new short[runtotal[i]+256]; disCont[i].bbIn[1]=new short[runtotal[i]+256]; disCont[i].bbInLen=runtotal[i]+256; From 153e025cf89841aabd1b9e47af2e592735eb21a4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:48:02 -0500 Subject: [PATCH 13/26] NES: finally --- src/engine/platform/nes.cpp | 1 + src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 084c1123c..9f44ebe05 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -145,6 +145,7 @@ void DivPlatformNES::acquire_NSFPlay(short* bufL, short* bufR, size_t start, siz doPCM; nes1_NP->Tick(1); + nes2_NP->TickFrameSequence(1); nes2_NP->Tick(1); nes1_NP->Render(out1); nes2_NP->Render(out2); diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp index a37b544d2..65742d0e5 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp @@ -53,7 +53,7 @@ namespace xgm option[OPT_NONLINEAR_MIXER] = 1; option[OPT_RANDOMIZE_NOISE] = 1; option[OPT_RANDOMIZE_TRI] = 1; - option[OPT_TRI_MUTE] = 1; + option[OPT_TRI_MUTE] = 0; option[OPT_DPCM_REVERSE] = 0; tnd_table[0][0][0][0] = 0; tnd_table[1][0][0][0] = 0; From e1cec62af4686b6e0efc7fdf47e749f63bd38cd7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:48:35 -0500 Subject: [PATCH 14/26] NES: now fix tri_mute --- src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp index 65742d0e5..a37b544d2 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp @@ -53,7 +53,7 @@ namespace xgm option[OPT_NONLINEAR_MIXER] = 1; option[OPT_RANDOMIZE_NOISE] = 1; option[OPT_RANDOMIZE_TRI] = 1; - option[OPT_TRI_MUTE] = 0; + option[OPT_TRI_MUTE] = 1; option[OPT_DPCM_REVERSE] = 0; tnd_table[0][0][0][0] = 0; tnd_table[1][0][0][0] = 0; From 61b4e3745ff82117dd546ac7b8cc8d41801070e3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:57:53 -0500 Subject: [PATCH 15/26] NES: NSFPlay muting --- src/engine/platform/nes.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 9f44ebe05..e353b26dc 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -150,7 +150,7 @@ void DivPlatformNES::acquire_NSFPlay(short* bufL, short* bufR, size_t start, siz nes1_NP->Render(out1); nes2_NP->Render(out2); - int sample=out1[0]+out1[1]+out2[0]+out2[1]; + int sample=(out1[0]+out1[1]+out2[0]+out2[1])<<1; if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; @@ -505,7 +505,12 @@ int DivPlatformNES::dispatch(DivCommand c) { void DivPlatformNES::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - if (!useNP) nes->muted[ch]=mute; + if (useNP) { + nes1_NP->SetMask(((int)isMuted[0])|(isMuted[1]<<1)); + nes2_NP->SetMask(((int)isMuted[2])|(isMuted[3]<<1)|(isMuted[4]<<2)); + } else { + nes->muted[ch]=mute; + } } void DivPlatformNES::forceIns() { @@ -555,6 +560,8 @@ void DivPlatformNES::reset() { if (useNP) { nes1_NP->Reset(); nes2_NP->Reset(); + nes1_NP->SetMask(((int)isMuted[0])|(isMuted[1]<<1)); + nes2_NP->SetMask(((int)isMuted[2])|(isMuted[3]<<1)|(isMuted[4]<<2)); } else { apu_turn_on(nes,apuType); nes->apu.cpu_cycles=0; From de09073232f20fe82166d85d2d73f1b58bc6c48b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 00:05:28 -0500 Subject: [PATCH 16/26] NES: NSFPlay per-channel osc --- src/engine/platform/nes.cpp | 8 ++++++++ src/engine/platform/sound/nes_nsfplay/nes_apu.h | 2 +- src/engine/platform/sound/nes_nsfplay/nes_dmc.h | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index e353b26dc..8ca8d1f1c 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -154,6 +154,14 @@ void DivPlatformNES::acquire_NSFPlay(short* bufL, short* bufR, size_t start, siz if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; + if (++writeOscBuf>=32) { + writeOscBuf=0; + oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11; + oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11; + oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<11; + oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<11; + oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<8; + } } } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_apu.h b/src/engine/platform/sound/nes_nsfplay/nes_apu.h index b302dd02c..20c4d3663 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_apu.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_apu.h @@ -27,7 +27,6 @@ namespace xgm unsigned int gclock; unsigned char reg[0x20]; - int out[2]; double rate, clock; int square_table[32]; // nonlinear mixer @@ -63,6 +62,7 @@ namespace xgm int calc_sqr (int ch, unsigned int clocks); public: + int out[2]; NES_APU (); ~NES_APU (); diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h index b0aa2ef92..f78dd9f4a 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h @@ -36,7 +36,6 @@ namespace xgm unsigned int len_reg; unsigned int adr_reg; std::function memory; - unsigned int out[3]; unsigned int daddress; unsigned int dlength; unsigned int data; @@ -91,6 +90,7 @@ namespace xgm inline unsigned int calc_noise (unsigned int clocks); public: + unsigned int out[3]; NES_DMC (); ~NES_DMC (); From 430d0329bd3274a5ab00a483c52acf9988d4b160 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 00:22:34 -0500 Subject: [PATCH 17/26] per-channel oscilloscope, part 10 OPZ --- src/engine/platform/sound/ymfm/ymfm_opz.h | 3 +++ src/engine/platform/tx81z.cpp | 14 ++++++++++++++ src/engine/platform/tx81z.h | 2 ++ 3 files changed, 19 insertions(+) diff --git a/src/engine/platform/sound/ymfm/ymfm_opz.h b/src/engine/platform/sound/ymfm/ymfm_opz.h index 4bc4663a0..5be148a38 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opz.h +++ b/src/engine/platform/sound/ymfm/ymfm_opz.h @@ -320,6 +320,9 @@ public: // generate one sample of sound void generate(output_data *output, uint32_t numsamples = 1); + // get the engine + fm_engine* debug_engine() { return &m_fm; } + protected: // internal state uint8_t m_address; // address register diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index d69dfc875..90f1d738b 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -147,6 +147,8 @@ const char* DivPlatformTX81Z::getEffectName(unsigned char effect) { void DivPlatformTX81Z::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; + ymfm::ym2414::fm_engine* fme=fm_ymfm->debug_engine(); + for (size_t h=start; hgenerate(&out_ymfm); + for (int i=0; i<8; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1)); + } + os[0]=out_ymfm.data[0]; if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; @@ -714,6 +720,10 @@ void* DivPlatformTX81Z::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformTX81Z::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformTX81Z::getRegisterPool() { return regPool; } @@ -789,6 +799,7 @@ int DivPlatformTX81Z::init(DivEngine* p, int channels, int sugRate, unsigned int skipRegisterWrites=false; for (int i=0; i<8; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); fm_ymfm=new ymfm::ym2414(iface); @@ -798,6 +809,9 @@ int DivPlatformTX81Z::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformTX81Z::quit() { + for (int i=0; i<8; i++) { + delete oscBuf[i]; + } delete fm_ymfm; } diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index 5df591f64..60ea66ae0 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -68,6 +68,7 @@ class DivPlatformTX81Z: public DivDispatch { chVolR(127) {} }; Channel chan[8]; + DivDispatchOscBuffer* oscBuf[8]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -102,6 +103,7 @@ class DivPlatformTX81Z: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); From 7a67730c291925e5408b3a596a5899e79f41736e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 00:23:03 -0500 Subject: [PATCH 18/26] damn it --- src/engine/platform/tx81z.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 90f1d738b..4391d49eb 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -787,6 +787,9 @@ void DivPlatformTX81Z::setFlags(unsigned int flags) { baseFreqOff=0; } rate=chipClock/64; + for (int i=0; i<8; i++) { + oscBuf[i]->rate=rate; + } } bool DivPlatformTX81Z::isStereo() { From 6380876b9a5557daf4ef96cb57d1efb63c0f34c3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 00:41:58 -0500 Subject: [PATCH 19/26] OPL: fix panning --- src/engine/platform/opl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 33c59eeb2..dfe83d364 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -705,7 +705,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (c.value==0 && c.value2==0) { chan[c.chan].pan=3; } else { - chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); + chan[c.chan].pan=(c.value>0)|((c.value2>0)<<1); } int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; if (isMuted[c.chan]) { From 34f7750c27112a7315059f27f64a05b8a0c51dab Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 01:15:42 -0500 Subject: [PATCH 20/26] OPZ: fix muting --- src/engine/platform/tx81z.cpp | 114 +++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 4391d49eb..0f02c5e22 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -198,10 +198,14 @@ void DivPlatformTX81Z::tick(bool sysTick) { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } } @@ -266,11 +270,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { if (chan[i].std.alg.had) { chan[i].state.alg=chan[i].std.alg.val; - if (isMuted[i]) { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40); - } else { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0:0x40)|(chan[i].chVolR<<7)); - } + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0:0x40)|(chan[i].chVolR<<7)); if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; @@ -287,11 +287,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (chan[i].std.fb.had) { chan[i].state.fb=chan[i].std.fb.val; - if (isMuted[i]) { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40); - } else { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0:0x40)|(chan[i].chVolR<<7)); - } + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0:0x40)|(chan[i].chVolR<<7)); } if (chan[i].std.fms.had) { chan[i].state.fms=chan[i].std.fms.val; @@ -331,10 +327,14 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (m.tl.had) { op.tl=127-m.tl.val; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } if (m.rs.had) { @@ -364,12 +364,8 @@ void DivPlatformTX81Z::tick(bool sysTick) { oldWrites[baseAddr+ADDR_TL]=-1; } } - if (isMuted[i]) { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x00); - } else { - //if (chan[i].keyOn) immWrite(0x08,i); - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x00|(chan[i].chVolR<<7)); - } + //if (chan[i].keyOn) immWrite(0x08,i); + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x00|(chan[i].chVolR<<7)); if (chan[i].hardReset && chan[i].keyOn) { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; @@ -411,12 +407,8 @@ void DivPlatformTX81Z::tick(bool sysTick) { chan[i].freqChanged=false; } if (chan[i].keyOn) { - if (isMuted[i]) { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - } else { - //immWrite(0x08,i); - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40|(chan[i].chVolR<<7)); - } + //immWrite(0x08,i); + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40|(chan[i].chVolR<<7)); chan[i].keyOn=false; } } @@ -424,13 +416,19 @@ void DivPlatformTX81Z::tick(bool sysTick) { void DivPlatformTX81Z::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - // TODO: use volume registers! - /* - if (isMuted[ch]) { - immWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); - } else { - immWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); - }*/ + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator op=chan[ch].state.op[i]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (isOutput[chan[ch].state.alg][i]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } } int DivPlatformTX81Z::dispatch(DivCommand c) { @@ -450,13 +448,17 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator op=chan[c.chan].state.op[i]; - if (isOutput[chan[c.chan].state.alg][i]) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); - } + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][i]) { + if (!chan[c.chan].active || chan[c.chan].insChanged) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } + } else { + if (chan[c.chan].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } if (chan[c.chan].insChanged) { @@ -512,10 +514,14 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][i]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } break; @@ -606,10 +612,14 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; - if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][c.value]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } break; } @@ -678,10 +688,14 @@ void DivPlatformTX81Z::forceIns() { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator op=chan[i].state.op[j]; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); From a0db8ab49af873cd10a714e6032000d428cb25e0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 01:22:49 -0500 Subject: [PATCH 21/26] OPZ: possibly fix fixed freq emulation --- src/engine/platform/sound/ymfm/ymfm_opz.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_opz.cpp b/src/engine/platform/sound/ymfm/ymfm_opz.cpp index 62a3d4d9c..cc7a7c9d5 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opz.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opz.cpp @@ -498,7 +498,7 @@ uint32_t opz_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opd // additional 12 bits of resolution; this calculation gives us, for // example, a frequency of 8.0009Hz when 8Hz is requested uint32_t substep = m_phase_substep[opoffs]; - substep += 75 * freq; + substep += 75 * 1024 * freq; phase_step = substep >> 12; m_phase_substep[opoffs] = substep & 0xfff; From b92ce84b3463d0acbe4bb4b8d64d628a1060a3a9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 02:12:02 -0500 Subject: [PATCH 22/26] OPZ: more fixed frequency mode fixes --- src/engine/platform/tx81z.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 0f02c5e22..788cfe245 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -315,7 +315,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (m.mult.had) { op.mult=m.mult.val; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); } if (m.rr.had) { op.rr=m.rr.val; @@ -343,7 +343,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (m.dt.had) { op.dt=m.dt.val; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); } if (m.d2r.had) { op.d2r=m.d2r.val; @@ -462,7 +462,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { } } if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); @@ -605,7 +605,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.mult=c.value2&15; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); break; } case DIV_CMD_FM_TL: { @@ -697,7 +697,7 @@ void DivPlatformTX81Z::forceIns() { rWrite(baseAddr+ADDR_TL,op.tl); } } - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); From a7b8f81da71e8667e1f1ce58560d61134b556359 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 02:12:14 -0500 Subject: [PATCH 23/26] NES: prepare for DPCM --- src/engine/platform/nes.cpp | 15 +++++++++++++-- src/engine/platform/nes.h | 1 + src/engine/platform/sound/nes/apu.h | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 8ca8d1f1c..6f71cad52 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -53,6 +53,10 @@ const char* regCheatSheetNES[]={ NULL }; +unsigned char _readDMC(void* user, unsigned short addr) { + return ((DivPlatformNES*)user)->readDMC(addr); +} + const char** DivPlatformNES::getRegisterSheet() { return regCheatSheetNES; } @@ -633,6 +637,11 @@ void DivPlatformNES::setNSFPlay(bool use) { useNP=use; } +unsigned char DivPlatformNES::readDMC(unsigned short addr) { + printf("read from DMC! %x\n",addr); + return 0; +} + int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; apuType=flags; @@ -643,12 +652,14 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int f 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->SetMemory([this](unsigned short addr, unsigned int& data) { + data=readDMC(addr); }); nes2_NP->SetAPU(nes1_NP); } else { nes=new struct NESAPU; + nes->readDMC=_readDMC; + nes->readDMCUser=this; } writeOscBuf=0; for (int i=0; i<5; i++) { diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 8b898c7fc..cb58c3869 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -93,6 +93,7 @@ class DivPlatformNES: public DivDispatch { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); float getPostAmp(); + unsigned char readDMC(unsigned short addr); void setNSFPlay(bool use); void setFlags(unsigned int flags); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/sound/nes/apu.h b/src/engine/platform/sound/nes/apu.h index 224ea2a04..d0f9c31f9 100644 --- a/src/engine/platform/sound/nes/apu.h +++ b/src/engine/platform/sound/nes/apu.h @@ -207,7 +207,7 @@ enum apu_mode { APU_60HZ, APU_48HZ }; break;\ }\ {\ - a->DMC.buffer = 0;\ + a->DMC.buffer = a->readDMC(a->readDMCUser,a->DMC.address);\ }\ /* incremento gli hwtick da compiere */\ if (hwtick) { hwtick[0] += tick; }\ @@ -525,6 +525,8 @@ EXTERNC struct NESAPU { _apuTriangle TR; _apuNoise NS; _apuDMC DMC; + void* readDMCUser; + unsigned char (*readDMC)(void*,unsigned short); unsigned char muted[5]; }; From dbe9bf25e7b2615b3890eae2ee00a26cb1318bed Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 02:54:23 -0500 Subject: [PATCH 24/26] fix bug caused by new renderSamples approach when doing switchMaster --- src/engine/engine.cpp | 1 + src/engine/platform/nes.cpp | 83 +++++++++++++++++++++++++++++++++---- src/engine/platform/nes.h | 8 ++++ src/engine/playback.cpp | 3 ++ 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b37ed86cd..364b0d135 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2366,6 +2366,7 @@ bool DivEngine::switchMaster() { } else { return false; } + renderSamples(); return true; } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 6f71cad52..9e87e2a5d 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -20,7 +20,8 @@ #include "nes.h" #include "sound/nes/cpu_inline.h" #include "../engine.h" -#include +#include "../../ta-log.h" +#include #include struct _nla_table nla_table; @@ -75,6 +76,9 @@ const char* DivPlatformNES::getEffectName(unsigned char effect) { case 0x14: return "14xy: Sweep down (x: time; y: shift)"; break; + case 0x18: + return "18xx: Select PCM/DPCM mode (0: PCM; 1: DPCM)"; + break; } return NULL; } @@ -89,7 +93,7 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) { } #define doPCM \ - if (dacSample!=-1) { \ + if (!dpcmMode && dacSample!=-1) { \ dacPeriod+=dacRate; \ if (dacPeriod>=rate) { \ DivSample* s=parent->getSample(dacSample); \ @@ -344,10 +348,10 @@ int DivPlatformNES::dispatch(DivCommand c) { dacSample=ins->amiga.initSample; if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0); break; } else { - if (dumpWrites) addWrite(0xffff0000,dacSample); + if (dumpWrites && !dpcmMode) addWrite(0xffff0000,dacSample); } dacPos=0; dacPeriod=0; @@ -366,16 +370,29 @@ int DivPlatformNES::dispatch(DivCommand c) { dacSample=12*sampleBank+chan[c.chan].note%12; if (dacSample>=parent->song.sampleLen) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0); break; } else { - if (dumpWrites) addWrite(0xffff0000,dacSample); + if (dumpWrites && !dpcmMode) addWrite(0xffff0000,dacSample); } dacPos=0; dacPeriod=0; dacRate=parent->getSample(dacSample)->rate; - if (dumpWrites) addWrite(0xffff0001,dacRate); + if (dumpWrites && !dpcmMode) addWrite(0xffff0001,dacRate); chan[c.chan].furnaceDac=false; + 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,15); + rWrite(0x4012,(dpcmAddr>>6)&0xff); + rWrite(0x4013,dpcmLen&0xff); + rWrite(0x4015,31); + dpcmBank=dpcmAddr>>14; + logV("writing DPCM: %x %x",dpcmAddr,dpcmLen); + } } break; } else if (c.chan==3) { // noise @@ -484,6 +501,10 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_NES_DMC: rWrite(0x4011,c.value&0x7f); + if (dumpWrites && dpcmMode) addWrite(0xffff0002,0); + break; + case DIV_CMD_SAMPLE_MODE: + dpcmMode=c.value; break; case DIV_CMD_SAMPLE_BANK: sampleBank=c.value; @@ -568,6 +589,8 @@ void DivPlatformNES::reset() { dacRate=0; dacSample=-1; sampleBank=0; + dpcmBank=0; + dpcmMode=false; if (useNP) { nes1_NP->Reset(); @@ -638,8 +661,46 @@ void DivPlatformNES::setNSFPlay(bool use) { } unsigned char DivPlatformNES::readDMC(unsigned short addr) { - printf("read from DMC! %x\n",addr); - return 0; + return dpcmMem[(addr&0x3fff)|((dpcmBank&15)<<14)]; +} + +const void* DivPlatformNES::getSampleMem(int index) { + return index==0?dpcmMem:NULL; +} + +size_t DivPlatformNES::getSampleMemCapacity(int index) { + return index==0?262144:0; +} + +size_t DivPlatformNES::getSampleMemUsage(int index) { + return index==0?dpcmMemLen:0; +} + +void DivPlatformNES::renderSamples() { + memset(dpcmMem,0,getSampleMemCapacity(0)); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->lengthDPCM+63)&(~0xff); + logV("%d padded length: %d",i,paddedLen); + if ((memPos&0x4000)!=((memPos+paddedLen)&0x4000)) { + memPos=(memPos+0x3fff)&0x4000; + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of DPCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(dpcmMem+memPos,s->dataDPCM,getSampleMemCapacity(0)-memPos); + logW("out of DPCM memory for sample %d!",i); + } else { + memcpy(dpcmMem+memPos,s->dataDPCM,paddedLen); + } + s->offDPCM=memPos; + memPos+=paddedLen; + } + dpcmMemLen=memPos; } int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -669,6 +730,10 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int f } setFlags(flags); + dpcmMem=new unsigned char[262144]; + dpcmMemLen=0; + dpcmBank=0; + init_nla_table(500,500); reset(); return 5; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index cb58c3869..75205548a 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -64,9 +64,13 @@ class DivPlatformNES: public DivDispatch { int dacPeriod, dacRate; unsigned int dacPos, dacAntiClick; int dacSample; + unsigned char* dpcmMem; + size_t dpcmMemLen; + unsigned char dpcmBank; unsigned char sampleBank; unsigned char writeOscBuf; unsigned char apuType; + bool dpcmMode; bool dacAntiClickOn; bool useNP; struct NESAPU* nes; @@ -101,6 +105,10 @@ class DivPlatformNES: 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(); ~DivPlatformNES(); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 48ffd08a0..f478f9a1a 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -330,6 +330,9 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe case 0x14: // sweep down dispatchCmd(DivCommand(DIV_CMD_NES_SWEEP,ch,1,effectVal)); break; + case 0x18: // DPCM mode + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,effectVal)); + break; default: return false; } From 5a724e49494d5e6a7da3267c316b77a3151b21c7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 03:42:40 -0500 Subject: [PATCH 25/26] NES: DPCM work! --- papers/doc/7-systems/nes.md | 6 +++- src/engine/platform/nes.cpp | 57 ++++++++++++++++++++++++++++++++----- src/engine/platform/nes.h | 1 + src/gui/debugWindow.cpp | 45 +++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 8 deletions(-) diff --git a/papers/doc/7-systems/nes.md b/papers/doc/7-systems/nes.md index 45ce95bc3..3ccca2647 100644 --- a/papers/doc/7-systems/nes.md +++ b/papers/doc/7-systems/nes.md @@ -18,4 +18,8 @@ also known as Famicom. It is a five-channel PSG: first two channels play pulse w - `14xy`: setup sweep down. - `x` is the time. - `y` is the shift. - - set to 0 to disable it. \ No newline at end of file + - set to 0 to disable it. +- `18xx`: set PCM channel mode. + - `00`: PCM (software). + - `01`: DPCM (hardware). + - when in DPCM mode, samples will sound muffled (due to its nature), availables pitches are limited and loop point is ignored. \ No newline at end of file diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 9e87e2a5d..0e336df0e 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -206,6 +206,25 @@ static unsigned char noiseTable[253]={ 15 }; +unsigned char DivPlatformNES::calcDPCMRate(int inRate) { + if (inRate<4450) return 0; + if (inRate<5000) return 1; + if (inRate<5400) return 2; + if (inRate<5900) return 3; + if (inRate<6650) return 4; + if (inRate<7450) return 5; + if (inRate<8100) return 6; + if (inRate<8800) return 7; + if (inRate<10200) return 8; + if (inRate<11700) return 9; + if (inRate<13300) return 10; + if (inRate<15900) return 11; + if (inRate<18900) return 12; + if (inRate<23500) return 13; + if (inRate<29000) return 14; + return 15; +} + void DivPlatformNES::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); @@ -333,6 +352,9 @@ 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 (dumpWrites) addWrite(0xffff0001,dacRate); } chan[4].freqChanged=false; @@ -363,6 +385,18 @@ int DivPlatformNES::dispatch(DivCommand c) { 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; @@ -386,12 +420,11 @@ int DivPlatformNES::dispatch(DivCommand c) { if (dpcmLen>255) dpcmLen=255; // write DPCM rWrite(0x4015,15); - rWrite(0x4010,15); + rWrite(0x4010,calcDPCMRate(dacRate)); rWrite(0x4012,(dpcmAddr>>6)&0xff); rWrite(0x4013,dpcmLen&0xff); rWrite(0x4015,31); dpcmBank=dpcmAddr>>14; - logV("writing DPCM: %x %x",dpcmAddr,dpcmLen); } } break; @@ -421,6 +454,7 @@ int DivPlatformNES::dispatch(DivCommand c) { if (c.chan==4) { dacSample=-1; if (dumpWrites) addWrite(0xffff0002,0); + if (dpcmMode && !skipRegisterWrites) rWrite(0x4015,15); } chan[c.chan].active=false; chan[c.chan].keyOff=true; @@ -501,10 +535,16 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_NES_DMC: rWrite(0x4011,c.value&0x7f); - if (dumpWrites && dpcmMode) addWrite(0xffff0002,0); break; case DIV_CMD_SAMPLE_MODE: dpcmMode=c.value; + if (dumpWrites && dpcmMode) addWrite(0xffff0002,0); + dacSample=-1; + rWrite(0x4015,15); + rWrite(0x4010,0); + rWrite(0x4012,0); + rWrite(0x4013,0); + rWrite(0x4015,31); break; case DIV_CMD_SAMPLE_BANK: sampleBank=c.value; @@ -682,10 +722,13 @@ void DivPlatformNES::renderSamples() { size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; - int paddedLen=(s->lengthDPCM+63)&(~0xff); + unsigned int paddedLen=(s->lengthDPCM+63)&(~0x3f); logV("%d padded length: %d",i,paddedLen); - if ((memPos&0x4000)!=((memPos+paddedLen)&0x4000)) { - memPos=(memPos+0x3fff)&0x4000; + if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) { + memPos=(memPos+0x3fff)&(~0x3fff); + } + if (paddedLen>4081) { + paddedLen=4096; } if (memPos>=getSampleMemCapacity(0)) { logW("out of DPCM memory for sample %d!",i); @@ -695,7 +738,7 @@ void DivPlatformNES::renderSamples() { memcpy(dpcmMem+memPos,s->dataDPCM,getSampleMemCapacity(0)-memPos); logW("out of DPCM memory for sample %d!",i); } else { - memcpy(dpcmMem+memPos,s->dataDPCM,paddedLen); + memcpy(dpcmMem+memPos,s->dataDPCM,MIN(s->lengthDPCM,paddedLen)); } s->offDPCM=memPos; memPos+=paddedLen; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 75205548a..a03efc7a3 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -81,6 +81,7 @@ class DivPlatformNES: public DivDispatch { friend void putDispatchChan(void*,int,int); void doWrite(unsigned short addr, unsigned char data); + unsigned char calcDPCMRate(int inRate); 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); diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 06184c0ba..7b232a9cf 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -21,6 +21,7 @@ #include "debug.h" #include "IconsFontAwesome4.h" #include +#include void FurnaceGUI::drawDebug() { static int bpOrder; @@ -141,6 +142,50 @@ void FurnaceGUI::drawDebug() { ImGui::Columns(); ImGui::TreePop(); } + if (ImGui::TreeNode("Sample Debug")) { + for (int i=0; isong.sampleLen; i++) { + DivSample* sample=e->getSample(i); + if (sample==NULL) { + ImGui::Text("%d: ",i); + continue; + } + ImGui::Text("%d: %s",i,sample->name.c_str()); + ImGui::Indent(); + ImGui::Text("rate: %d",sample->rate); + ImGui::Text("centerRate: %d",sample->centerRate); + ImGui::Text("loopStart: %d",sample->loopStart); + ImGui::Text("loopOffP: %d",sample->loopOffP); + ImGui::Text("depth: %d",sample->depth); + ImGui::Text("length8: %d",sample->length8); + ImGui::Text("length16: %d",sample->length16); + ImGui::Text("length1: %d",sample->length1); + ImGui::Text("lengthDPCM: %d",sample->lengthDPCM); + ImGui::Text("lengthQSoundA: %d",sample->lengthQSoundA); + ImGui::Text("lengthA: %d",sample->lengthA); + ImGui::Text("lengthB: %d",sample->lengthB); + ImGui::Text("lengthX68: %d",sample->lengthX68); + ImGui::Text("lengthBRR: %d",sample->lengthBRR); + ImGui::Text("lengthVOX: %d",sample->lengthVOX); + + ImGui::Text("off8: %x",sample->off8); + ImGui::Text("off16: %x",sample->off16); + ImGui::Text("off1: %x",sample->off1); + ImGui::Text("offDPCM: %x",sample->offDPCM); + ImGui::Text("offQSoundA: %x",sample->offQSoundA); + ImGui::Text("offA: %x",sample->offA); + ImGui::Text("offB: %x",sample->offB); + ImGui::Text("offX68: %x",sample->offX68); + ImGui::Text("offBRR: %x",sample->offBRR); + ImGui::Text("offVOX: %x",sample->offVOX); + ImGui::Text("offSegaPCM: %x",sample->offSegaPCM); + ImGui::Text("offQSound: %x",sample->offQSound); + ImGui::Text("offX1_010: %x",sample->offX1_010); + + ImGui::Text("samples: %d",sample->samples); + ImGui::Unindent(); + } + ImGui::TreePop(); + } if (ImGui::TreeNode("Playground")) { if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0; if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) { From 66f6ab4307d26a77767e2a53a77cb610ef3b1648 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 03:52:45 -0500 Subject: [PATCH 26/26] prepare for #38 --- TODO.md | 1 - src/engine/dispatch.h | 15 +++++++++++++++ src/engine/playback.cpp | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 18c43c49d..54e966303 100644 --- a/TODO.md +++ b/TODO.md @@ -30,7 +30,6 @@ - store edit/followOrders/followPattern state in config - add ability to select a column by double clicking - add ability to move selection by dragging -- NSFPlay core for NES - settings: OK/Cancel buttons should be always visible - Apply button in settings - better FM chip names (number and codename) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c91b56efd..2646a7947 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -64,9 +64,24 @@ enum DivDispatchCmds { DIV_CMD_FM_LFO, // (speed) DIV_CMD_FM_LFO_WAVE, // (waveform) DIV_CMD_FM_TL, // (op, value) + DIV_CMD_FM_AM, // (op, value) DIV_CMD_FM_AR, // (op, value) + DIV_CMD_FM_DR, // (op, value) + DIV_CMD_FM_SL, // (op, value) + DIV_CMD_FM_D2R, // (op, value) + DIV_CMD_FM_RR, // (op, value) + DIV_CMD_FM_DT, // (op, value) + DIV_CMD_FM_DT2, // (op, value) + DIV_CMD_FM_RS, // (op, value) + DIV_CMD_FM_KSR, // (op, value) + DIV_CMD_FM_VIB, // (op, value) + DIV_CMD_FM_SUS, // (op, value) + DIV_CMD_FM_WS, // (op, value) + DIV_CMD_FM_SSG, // (op, value) DIV_CMD_FM_FB, // (value) DIV_CMD_FM_MULT, // (op, value) + DIV_CMD_FM_FINE, // (op, value) + DIV_CMD_FM_FIXFREQ, // (op, value) DIV_CMD_FM_EXTCH, // (enabled) DIV_CMD_FM_AM_DEPTH, // (depth) DIV_CMD_FM_PM_DEPTH, // (depth) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index f478f9a1a..c959f64ac 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -65,9 +65,24 @@ const char* cmdName[]={ "FM_LFO", "FM_LFO_WAVE", "FM_TL", + "FM_AM", "FM_AR", + "FM_DR", + "FM_SL", + "FM_D2R", + "FM_RR", + "FM_DT", + "FM_DT2", + "FM_RS", + "FM_KSR", + "FM_VIB", + "FM_SUS", + "FM_WS", + "FM_SSG", "FM_FB", "FM_MULT", + "FM_FINE", + "FM_FIXFREQ", "FM_EXTCH", "FM_AM_DEPTH", "FM_PM_DEPTH",