From 216acd5ec5a8130f9d9f04dea5623db3a7720a81 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Tue, 13 Sep 2022 09:21:16 +0000 Subject: [PATCH 01/63] fix n163 doc 256 bytes, not 128. https://www.nesdev.org/wiki/Namco_163_audio also some better wording --- papers/doc/7-systems/n163.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/7-systems/n163.md b/papers/doc/7-systems/n163.md index 3c2f389f9..78aca3ae6 100644 --- a/papers/doc/7-systems/n163.md +++ b/papers/doc/7-systems/n163.md @@ -1,6 +1,6 @@ # Namco 163 (also called N163, Namco C163, Namco 106 (sic), Namco 160 or Namco 129) -This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 128 byte of internal RAM, and both channel register and wavetables are stored here. Wavetables are variable size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. But waveform RAM area becomes smaller as more channels are activated; as channel registers consumes 8 bytes for each channel. You must avoid conflict with channel register area and waveform for avoid broken channel playback. +This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 256 bytes of internal RAM, and both channel register and wavetables are stored here. Wavetables are variable in size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. But waveform RAM area becomes smaller as more channels are activated; as channel registers consume 8 bytes for each channel. You must avoid conflict with channel register area and waveform to avoid broken channel playback. It outputs only a single channel at clock; so its sound quality gets more crunchy as more channels are activated. From f60e650a91070cdd7914150e7d1888c6bc1707d5 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Tue, 13 Sep 2022 19:18:08 +0000 Subject: [PATCH 02/63] correct doc no brainwashing --- papers/doc/7-systems/n163.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/papers/doc/7-systems/n163.md b/papers/doc/7-systems/n163.md index 78aca3ae6..fb9502deb 100644 --- a/papers/doc/7-systems/n163.md +++ b/papers/doc/7-systems/n163.md @@ -1,8 +1,8 @@ # Namco 163 (also called N163, Namco C163, Namco 106 (sic), Namco 160 or Namco 129) -This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 256 bytes of internal RAM, and both channel register and wavetables are stored here. Wavetables are variable in size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. But waveform RAM area becomes smaller as more channels are activated; as channel registers consume 8 bytes for each channel. You must avoid conflict with channel register area and waveform to avoid broken channel playback. +This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 256 nibbles (128 bytes) of internal RAM, and both channel registers and wavetables are stored here. Wavetables are variable in size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. At least 64 bytes can dedicated to waves, with more available if not all channels are used - waveform RAM area becomes smaller as more channels are activated, since channel registers consume 8 bytes for each channel. You must avoid conflict with channel register area and waveform to avoid broken channel playback. -It outputs only a single channel at clock; so its sound quality gets more crunchy as more channels are activated. +Namco 163 does not internally mix its channels. Instead, each channel is output one at a time, when multiple channels are used it will cycle between them; so its sound quality gets more crunchy as more channels are activated. Furnace supports loading waveforms into RAM and waveform playback simultaneously, and channel limit is dynamically changeable with effect commands. You must load waveform to RAM first for playback, as its load behavior auto-updates when every waveform changes. From 6a735ee3487068f4fface2ee4b38611f867472a2 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Tue, 13 Sep 2022 19:25:37 +0000 Subject: [PATCH 03/63] no noise for MMC5 --- papers/doc/7-systems/mmc5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/7-systems/mmc5.md b/papers/doc/7-systems/mmc5.md index faf305c60..5949dbb94 100644 --- a/papers/doc/7-systems/mmc5.md +++ b/papers/doc/7-systems/mmc5.md @@ -9,4 +9,4 @@ additionally, it offers an 8-bit DAC which can be used to play samples. only one # effects - `12xx`: set duty cycle or noise mode of channel. - - may be 0-3 for the pulse channels and 0-1 for the noise channel. + - may be 0-3 for the pulse channels From 70ca9033c76bef4d8624f09d11b1cb1f13209782 Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Tue, 13 Sep 2022 19:58:43 +0000 Subject: [PATCH 04/63] Add Generic PCM DAC document --- papers/doc/7-systems/dac.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 papers/doc/7-systems/dac.md diff --git a/papers/doc/7-systems/dac.md b/papers/doc/7-systems/dac.md new file mode 100644 index 000000000..856db2cd0 --- /dev/null +++ b/papers/doc/7-systems/dac.md @@ -0,0 +1,7 @@ +# Generic PCM DAC + +Realtek HD Audio's predecessor. It's just a 1/8/16-bit sample channel, with freely selectable rate and mono/stereo settings. With it, you can emulate PCM DACs found in Williams arcade boards, Sound Blasters, MSX TurboR, Atari STE, NEC PC-9801-86 etc. + +# effects + +none yet. From 359fda701677d83f1d9c58a96da5e3ca2c34825b Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:04:28 +0000 Subject: [PATCH 05/63] addressing feedback --- papers/doc/7-systems/mmc5.md | 2 +- papers/doc/7-systems/n163.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/papers/doc/7-systems/mmc5.md b/papers/doc/7-systems/mmc5.md index 5949dbb94..d483bd57c 100644 --- a/papers/doc/7-systems/mmc5.md +++ b/papers/doc/7-systems/mmc5.md @@ -9,4 +9,4 @@ additionally, it offers an 8-bit DAC which can be used to play samples. only one # effects - `12xx`: set duty cycle or noise mode of channel. - - may be 0-3 for the pulse channels + - may be 0-3 for the pulse channels. diff --git a/papers/doc/7-systems/n163.md b/papers/doc/7-systems/n163.md index fb9502deb..90f432289 100644 --- a/papers/doc/7-systems/n163.md +++ b/papers/doc/7-systems/n163.md @@ -1,8 +1,8 @@ # Namco 163 (also called N163, Namco C163, Namco 106 (sic), Namco 160 or Namco 129) -This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 256 nibbles (128 bytes) of internal RAM, and both channel registers and wavetables are stored here. Wavetables are variable in size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. At least 64 bytes can dedicated to waves, with more available if not all channels are used - waveform RAM area becomes smaller as more channels are activated, since channel registers consume 8 bytes for each channel. You must avoid conflict with channel register area and waveform to avoid broken channel playback. +This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 256 nibbles (128 bytes) of internal RAM, and both channel registers and wavetables are stored here. Wavetables are variable in size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. At least 128 nibbles (64 bytes) can be dedicated to waves, with more available if not all channels are used - waveform RAM area becomes smaller as more channels are activated, since channel registers consume 8 bytes for each channel. You must avoid conflict with channel register area and waveform to avoid broken channel playback. -Namco 163 does not internally mix its channels. Instead, each channel is output one at a time, when multiple channels are used it will cycle between them; so its sound quality gets more crunchy as more channels are activated. +Namco 163 does not internally mix its channels. Because of that, only one channel can be output at the time, when multiple channels are used it will cycle between them, similarily to OPLL. Therefore, its sound quality gets more crunchy as more channels are activated. Furnace supports loading waveforms into RAM and waveform playback simultaneously, and channel limit is dynamically changeable with effect commands. You must load waveform to RAM first for playback, as its load behavior auto-updates when every waveform changes. From aa38292ca4e91aeedcefdc32701389c99deac521 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Sep 2022 00:04:43 -0500 Subject: [PATCH 06/63] GUI: fix OPLL/OPL op swapping --- src/gui/insEdit.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a4b99d141..df92dd523 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1579,15 +1579,17 @@ void FurnaceGUI::drawMacros(std::vector& macros) { if (dragItem!=NULL) { \ if (dragItem->IsDataType("FUR_OP")) { \ if (opToMove!=i && opToMove>=0) { \ + int destOp=(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i; \ + int sourceOp=(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[opToMove]:opToMove; \ if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { \ - e->lockEngine([this,ins,i]() { \ - ins->fm.op[orderedOps[i]]=ins->fm.op[orderedOps[opToMove]]; \ + e->lockEngine([ins,destOp,sourceOp]() { \ + ins->fm.op[destOp]=ins->fm.op[sourceOp]; \ }); \ } else { \ - e->lockEngine([this,ins,i]() { \ - DivInstrumentFM::Operator origOp=ins->fm.op[orderedOps[opToMove]]; \ - ins->fm.op[orderedOps[opToMove]]=ins->fm.op[orderedOps[i]]; \ - ins->fm.op[orderedOps[i]]=origOp; \ + e->lockEngine([ins,destOp,sourceOp]() { \ + DivInstrumentFM::Operator origOp=ins->fm.op[sourceOp]; \ + ins->fm.op[sourceOp]=ins->fm.op[destOp]; \ + ins->fm.op[destOp]=origOp; \ }); \ } \ PARAMETER; \ From 587e066d4300578cc7701bb959399f18670143b2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Sep 2022 00:18:14 -0500 Subject: [PATCH 07/63] GUI: randomize in macros under the right click --- src/gui/gui.cpp | 2 ++ src/gui/gui.h | 1 + src/gui/insEdit.cpp | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 4785ee818..5153e1f6a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5216,6 +5216,8 @@ FurnaceGUI::FurnaceGUI(): macroOffY(0), macroScaleX(100.0f), macroScaleY(100.0f), + macroRandMin(0), + macroRandMax(0), macroLoopDragStart(0,0), macroLoopDragAreaSize(0,0), macroLoopDragTarget(NULL), diff --git a/src/gui/gui.h b/src/gui/gui.h index b71fe368d..1c8ee713f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1446,6 +1446,7 @@ class FurnaceGUI { FurnaceGUIMacroDesc lastMacroDesc; int macroOffX, macroOffY; float macroScaleX, macroScaleY; + int macroRandMin, macroRandMax; ImVec2 macroLoopDragStart; ImVec2 macroLoopDragAreaSize; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index df92dd523..b5347cbee 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -4556,6 +4556,28 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndMenu(); } + if (ImGui::BeginMenu("randomize...")) { + if (macroRandMinlastMacroDesc.max) macroRandMin=lastMacroDesc.max; + if (macroRandMaxlastMacroDesc.max) macroRandMax=lastMacroDesc.max; + ImGui::InputInt("Min",¯oRandMin,1,10); + ImGui::InputInt("Max",¯oRandMax,1,10); + if (ImGui::Button("randomize")) { + for (int i=0; ilen; i++) { + int val=0; + if (macroRandMax<=macroRandMin) { + val=macroRandMin; + } else { + val=macroRandMin+(rand()%(macroRandMax-macroRandMin+1)); + } + lastMacroDesc.macro->val[i]=val; + } + + ImGui::CloseCurrentPopup(); + } + ImGui::EndMenu(); + } ImGui::EndPopup(); } From 6b0aee8cf7cb6f6ea89fd02c42939b80f1fc02f4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Sep 2022 02:00:42 -0500 Subject: [PATCH 08/63] OPL: "fix" stereo in OPL1/2 --- src/engine/platform/opl.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index eb49637e3..2dd6d1dcf 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -249,7 +249,9 @@ void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_ if (os[1]>32767) os[1]=32767; bufL[h]=os[0]; - bufR[h]=os[1]; + if (oplType==3 || oplType==759) { + bufR[h]=os[1]; + } } } @@ -1520,7 +1522,7 @@ void DivPlatformOPL::reset() { } bool DivPlatformOPL::isStereo() { - return true; + return (oplType==3 || oplType==759); } bool DivPlatformOPL::keyOffAffectsArp(int ch) { From 3e311d94a208108b0c07aa8d3406b73b8593e2ac Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 16 Sep 2022 02:04:01 -0500 Subject: [PATCH 09/63] GUI: fix FM wave gen mult 16 --- src/gui/waveEdit.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index 006ee4304..87f5ad38f 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -32,7 +32,7 @@ const char* waveGenBaseShapes[4]={ "Pulse" }; -const float multFactors[16]={ +const float multFactors[17]={ M_PI, 2*M_PI, 4*M_PI, @@ -49,6 +49,7 @@ const float multFactors[16]={ 26*M_PI, 28*M_PI, 30*M_PI, + 32*M_PI, }; void FurnaceGUI::doGenerateWave() { From b461ffe41141f4b90ee4f40899bc87350783cf56 Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 16 Sep 2022 23:48:06 +0900 Subject: [PATCH 10/63] Update vgsound_emu library --- CMakeLists.txt | 37 +- src/engine/platform/bubsyswsg.cpp | 12 +- src/engine/platform/bubsyswsg.h | 4 +- src/engine/platform/msm6295.cpp | 14 +- src/engine/platform/msm6295.h | 4 +- src/engine/platform/n163.cpp | 2 +- src/engine/platform/n163.h | 2 +- src/engine/platform/scc.cpp | 2 +- src/engine/platform/scc.h | 2 +- src/engine/platform/sound/k005289/k005289.cpp | 46 -- src/engine/platform/sound/k005289/k005289.hpp | 68 -- src/engine/platform/sound/n163/n163.cpp | 167 ----- src/engine/platform/sound/n163/n163.hpp | 90 --- src/engine/platform/sound/oki/msm6295.cpp | 215 ------ src/engine/platform/sound/oki/msm6295.hpp | 95 --- src/engine/platform/sound/oki/util.hpp | 159 ----- src/engine/platform/sound/oki/vox.hpp | 116 ---- src/engine/platform/sound/scc/scc.cpp | 617 ------------------ src/engine/platform/sound/scc/scc.hpp | 139 ---- src/engine/platform/sound/vrcvi/vrcvi.cpp | 324 --------- src/engine/platform/sound/vrcvi/vrcvi.hpp | 242 ------- src/engine/platform/sound/x1_010/x1_010.cpp | 225 ------- src/engine/platform/sound/x1_010/x1_010.hpp | 134 ---- src/engine/platform/vrc6.cpp | 7 +- src/engine/platform/vrc6.h | 7 +- src/engine/platform/x1_010.cpp | 28 +- src/engine/platform/x1_010.h | 23 +- src/main.cpp | 2 +- 28 files changed, 78 insertions(+), 2705 deletions(-) delete mode 100644 src/engine/platform/sound/k005289/k005289.cpp delete mode 100644 src/engine/platform/sound/k005289/k005289.hpp delete mode 100644 src/engine/platform/sound/n163/n163.cpp delete mode 100644 src/engine/platform/sound/n163/n163.hpp delete mode 100644 src/engine/platform/sound/oki/msm6295.cpp delete mode 100644 src/engine/platform/sound/oki/msm6295.hpp delete mode 100644 src/engine/platform/sound/oki/util.hpp delete mode 100644 src/engine/platform/sound/oki/vox.hpp delete mode 100644 src/engine/platform/sound/scc/scc.cpp delete mode 100644 src/engine/platform/sound/scc/scc.hpp delete mode 100644 src/engine/platform/sound/vrcvi/vrcvi.cpp delete mode 100644 src/engine/platform/sound/vrcvi/vrcvi.hpp delete mode 100644 src/engine/platform/sound/x1_010/x1_010.cpp delete mode 100644 src/engine/platform/sound/x1_010/x1_010.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7db42e6..39d3321cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ else() endif() list(APPEND DEPENDENCIES_INCLUDE_DIRS "extern/SAASound/include") +list(APPEND DEPENDENCIES_INCLUDE_DIRS "extern/vgsound_emu-modified") find_package(Threads REQUIRED) list(APPEND DEPENDENCIES_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) @@ -319,6 +320,31 @@ extern/SAASound/src/SAANoise.cpp extern/SAASound/src/SAASndC.cpp extern/SAASound/src/SAASound.cpp +extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp +extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp + +extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp + extern/adpcm/bs_codec.c extern/adpcm/oki_codec.c extern/adpcm/yma_codec.c @@ -398,28 +424,17 @@ src/engine/platform/sound/lynx/Mikey.cpp src/engine/platform/sound/qsound.c -src/engine/platform/sound/x1_010/x1_010.cpp - src/engine/platform/sound/swan.cpp src/engine/platform/sound/su.cpp -src/engine/platform/sound/k005289/k005289.cpp - -src/engine/platform/sound/n163/n163.cpp - src/engine/platform/sound/vic20sound.c -src/engine/platform/sound/vrcvi/vrcvi.cpp - -src/engine/platform/sound/scc/scc.cpp - src/engine/platform/sound/ymz280b.cpp src/engine/platform/sound/rf5c68.cpp src/engine/platform/sound/oki/okim6258.cpp -src/engine/platform/sound/oki/msm6295.cpp src/engine/platform/oplAInterface.cpp src/engine/platform/ym2608Interface.cpp diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 89503cceb..89d609cec 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -44,7 +44,7 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_ for (size_t h=start; htick(); + k005289.tick(); // Wavetable part for (int i=0; i<2; i++) { @@ -52,7 +52,7 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_ oscBuf[i]->data[oscBuf[i]->needle++]=0; continue; } else { - chanOut=chan[i].waveROM[k005289->addr(i)]*(regPool[2+i]&0xf); + chanOut=chan[i].waveROM[k005289.addr(i)]*(regPool[2+i]&0xf); out+=chanOut; if (writeOscBuf==0) { oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<7; @@ -122,9 +122,9 @@ void DivPlatformBubSysWSG::tick(bool sysTick) { chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>4095) chan[i].freq=4095; - k005289->load(i,chan[i].freq); + k005289.load(i,chan[i].freq); rWrite(i,chan[i].freq); - k005289->update(i); + k005289.update(i); if (chan[i].keyOn) { // ??? } @@ -295,7 +295,7 @@ void DivPlatformBubSysWSG::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } - k005289->reset(); + k005289.reset(); } bool DivPlatformBubSysWSG::isStereo() { @@ -347,7 +347,6 @@ int DivPlatformBubSysWSG::init(DivEngine* p, int channels, int sugRate, unsigned oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); - k005289=new k005289_core(); reset(); return 2; } @@ -356,7 +355,6 @@ void DivPlatformBubSysWSG::quit() { for (int i=0; i<2; i++) { delete oscBuf[i]; } - delete k005289; } DivPlatformBubSysWSG::~DivPlatformBubSysWSG() { diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index cfc41dc12..f14b94a77 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -24,7 +24,7 @@ #include #include "../macroInt.h" #include "../waveSynth.h" -#include "sound/k005289/k005289.hpp" +#include "vgsound_emu/src/k005289/k005289.hpp" class DivPlatformBubSysWSG: public DivDispatch { struct Channel { @@ -60,7 +60,7 @@ class DivPlatformBubSysWSG: public DivDispatch { bool isMuted[2]; unsigned char writeOscBuf; - k005289_core* k005289; + k005289_core k005289; unsigned short regPool[4]; void updateWave(int ch); friend void putDispatchChan(void*,int,int); diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index 0a76a2c9b..4d32f4319 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -79,7 +79,7 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t updateOsc=0; // TODO: per-channel osc for (int i=0; i<4; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=msm.m_voice[i].m_muted?0:(msm.m_voice[i].m_out<<6); + oscBuf[i]->data[oscBuf[i]->needle++]=msm.voice_out(i)<<6; } } } @@ -113,7 +113,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - rWriteDelay(0,(8<getSample(12*sampleBank+c.value%12); chan[c.chan].sample=12*sampleBank+c.value%12; - rWriteDelay(0,(8<rate=rate/22; } diff --git a/src/engine/platform/msm6295.h b/src/engine/platform/msm6295.h index 0953bd33e..33f5c85fb 100644 --- a/src/engine/platform/msm6295.h +++ b/src/engine/platform/msm6295.h @@ -22,7 +22,7 @@ #include "../dispatch.h" #include "../macroInt.h" #include -#include "sound/oki/msm6295.hpp" +#include "vgsound_emu/src/msm6295/msm6295.hpp" class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { protected: @@ -57,7 +57,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { unsigned short addr; unsigned char val; unsigned short delay; - QueuedWrite(unsigned short a, unsigned char v, unsigned short d=32): + QueuedWrite(unsigned short a, unsigned char v, unsigned short d=96): addr(a), val(v), delay(d) {} diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index bc985b42b..1597d0fa2 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -117,7 +117,7 @@ void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len bufL[i]=bufR[i]=out; if (n163.voice_cycle()==0x78) for (int i=0; i<8; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=n163.chan_out(i)<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<7; } // command queue diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index c77510f56..d4a9ee357 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -24,7 +24,7 @@ #include #include "../macroInt.h" #include "../waveSynth.h" -#include "sound/n163/n163.hpp" +#include "vgsound_emu/src/n163/n163.hpp" class DivPlatformN163: public DivDispatch { struct Channel { diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index d8859cffd..725c53da9 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -89,7 +89,7 @@ void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len) bufL[h]=bufR[h]=out; for (int i=0; i<5; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=scc->chan_out(i)<<7; + oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<7; } } } diff --git a/src/engine/platform/scc.h b/src/engine/platform/scc.h index f9fa31725..b6117d4a1 100644 --- a/src/engine/platform/scc.h +++ b/src/engine/platform/scc.h @@ -24,7 +24,7 @@ #include #include "../macroInt.h" #include "../waveSynth.h" -#include "sound/scc/scc.hpp" +#include "vgsound_emu/src/scc/scc.hpp" class DivPlatformSCC: public DivDispatch { struct Channel { diff --git a/src/engine/platform/sound/k005289/k005289.cpp b/src/engine/platform/sound/k005289/k005289.cpp deleted file mode 100644 index c9bdf83ae..000000000 --- a/src/engine/platform/sound/k005289/k005289.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900 - Konami K005289 emulation core - - This chip is used at infamous Konami Bubble System, for part of Wavetable sound generator. - But seriously, It is just to 2 internal 12 bit timer and address generators, rather than sound generator. - - Everything except for internal counter and address are done by external logic, the chip is only has external address, frequency registers and its update pins. - - Frequency calculation: Input clock / (4096 - Pitch input) -*/ - -#include "k005289.hpp" - -void k005289_core::tick() -{ - for (auto & elem : m_voice) - elem.tick(); -} - -void k005289_core::reset() -{ - for (auto & elem : m_voice) - elem.reset(); -} - -void k005289_core::voice_t::tick() -{ - if (bitfield(++counter, 0, 12) == 0) - { - addr = bitfield(addr + 1, 0, 5); - counter = freq; - } -} - -void k005289_core::voice_t::reset() -{ - addr = 0; - pitch = 0; - freq = 0; - counter = 0; -} diff --git a/src/engine/platform/sound/k005289/k005289.hpp b/src/engine/platform/sound/k005289/k005289.hpp deleted file mode 100644 index 575a98b87..000000000 --- a/src/engine/platform/sound/k005289/k005289.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900 - Konami K005289 emulation core - - See k005289.cpp for more info. -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_K005289_HPP -#define _VGSOUND_EMU_K005289_HPP - -#pragma once - -namespace k005289 -{ - typedef unsigned char u8; - typedef unsigned short u16; - typedef signed short s16; - - // get bitfield, bitfield(input, position, len) - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -} - -using namespace k005289; -class k005289_core -{ -public: - // accessors, getters, setters - u8 addr(int voice) { return m_voice[voice & 1].addr; } // 1QA...E/2QA...E pin - void load(int voice, u16 addr) { m_voice[voice & 1].load(addr); } // LD1/2 pin, A0...11 pin - void update(int voice) { m_voice[voice & 1].update(); } // TG1/2 pin - - // internal state - void reset(); - void tick(); - -private: - // k005289 voice structs - struct voice_t - { - // internal state - void reset(); - void tick(); - - // accessors, getters, setters - void load(u16 addr) { pitch = addr; } // Load pitch data (address pin) - void update() { freq = pitch; } // Replace current frequency to lastest loaded pitch - - // registers - u8 addr = 0; // external address pin - u16 pitch = 0; // pitch - u16 freq = 0; // current frequency - s16 counter = 0; // frequency counter - }; - - voice_t m_voice[2]; -}; - -#endif diff --git a/src/engine/platform/sound/n163/n163.cpp b/src/engine/platform/sound/n163/n163.cpp deleted file mode 100644 index 3fd30bc44..000000000 --- a/src/engine/platform/sound/n163/n163.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Namco 163 Sound emulation core - - This chip is one of NES mapper with sound expansion, This one is by Namco. - - It has 1 to 8 wavetable channels, All channel registers and waveforms are stored to internal RAM. - 4 bit Waveforms are freely allocatable, and its length is variables; its can be stores many short waveforms or few long waveforms in RAM. - - But waveforms are needs to squash, reallocate to avoid conflict with channel register area, each channel register size is 8 bytes per channels. - - Sound output is time division multiplexed, it's can be captured only single channels output at once. in reason, More activated channels are less sound quality. - - Sound register layout - - Address Bit Description - 7654 3210 - - 78-7f Channel 0 - 78 xxxx xxxx Channel 0 Pitch input bit 0-7 - 79 xxxx xxxx Channel 0 Accumulator bit 0-7* - 7a xxxx xxxx Channel 0 Pitch input bit 8-15 - 7b xxxx xxxx Channel 0 Accumulator bit 8-15* - 7c xxxx xx-- Channel 0 Waveform length, 256 - (x * 4) - ---- --xx Channel 0 Pitch input bit 16-17 - 7d xxxx xxxx Channel 0 Accumulator bit 16-23* - 7e xxxx xxxx Channel 0 Waveform base offset - xxxx xxx- RAM byte (0 to 127) - ---- ---x RAM nibble - ---- ---0 Low nibble - ---- ---1 High nibble - 7f ---- xxxx Channel 0 Volume - - 7f Number of active channels - 7f -xxx ---- Number of active channels - -000 ---- Channel 0 activated - -001 ---- Channel 1 activated - -010 ---- Channel 2 activated - ... - -110 ---- Channel 6 activated - -111 ---- Channel 7 activated - - 70-77 Channel 1 (Optional if activated) - 68-6f Channel 2 (Optional if activated) - ... - 48-4f Channel 6 (Optional if activated) - 40-47 Channel 7 (Optional if activated) - - Rest of RAM area are for 4 bit Waveform and/or scratchpad. - Each waveform byte has 2 nibbles packed, fetches LSB first, MSB next. - ---- xxxx 4 bit waveform, LSB - xxxx ---- Same as above, MSB - - Waveform address: Waveform base offset + Bit 16 to 23 of Accumulator, 1 LSB of result is nibble select, 7 MSB of result is Byte address in RAM. - - Frequency formula: - Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536) - - There's to way for reduce N163 noises: reduce channel limit and demultiplex - - Channel limit is runtime changeable and it makes some usable effects. - - Demultiplex is used for "non-ear destroyable" emulators, but less hardware accurate. (when LPF and RF filter is not considered) - This core is support both, You can choose output behavior - -*/ - -#include "n163.hpp" -#include - -void n163_core::tick() -{ - if (m_multiplex) - m_out = 0; - // 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank 0x8000-0x9fff select.) - if (m_disable) - { - if (!m_multiplex) - m_out = 0; - return; - } - - // tick per each clock - const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | (bitfield(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency - u32 accum = m_ram[m_voice_cycle + 1] | (u32(m_ram[m_voice_cycle + 3]) << 8) | ( u32(m_ram[m_voice_cycle + 5]) << 16); // 24 bit accumulator - const u16 length = 256 - (m_ram[m_voice_cycle + 4] & 0xfc); - const u8 addr = m_ram[m_voice_cycle + 6] + bitfield(accum, 16, 8); - const s16 wave = (bitfield(m_ram[bitfield(addr, 1, 7)], bitfield(addr, 0) << 2, 4) - 8); - const s16 volume = bitfield(m_ram[m_voice_cycle + 7], 0, 4); - - // accumulate address - accum = bitfield(accum + freq, 0, 24); - if (bitfield(accum, 16, 8) >= length) - accum = bitfield(accum, 0, 18); - - // writeback to register - m_ram[m_voice_cycle + 1] = bitfield(accum, 0, 8); - m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8); - m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8); - - const u8 prev_voice_cycle = m_voice_cycle; - - // update voice cycle - bool flush = m_multiplex ? true : false; - m_voice_cycle -= 0x8; - if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3))) - { - if (!m_multiplex) - flush = true; - m_voice_cycle = 0x78; - } - - // output 4 bit waveform and volume, multiplexed - const u8 chan_index = ((0x78-prev_voice_cycle)>>3)&7; - m_ch_out[chan_index]=wave * volume; - m_acc += m_ch_out[chan_index]; - if (flush) - { - m_out = m_acc / (m_multiplex ? 1 : (bitfield(m_ram[0x7f], 4, 3) + 1)); - m_acc = 0; - } -} - -void n163_core::reset() -{ - // reset this chip - m_disable = false; - m_multiplex = true; - memset(m_ram,0,sizeof(m_ram)); - m_voice_cycle = 0x78; - m_addr_latch.reset(); - m_out = 0; - m_acc = 0; - memset(m_ch_out,0,sizeof(m_ch_out)); -} - -// accessor -void n163_core::addr_w(u8 data) -{ - // 0xf800-0xffff Sound address, increment - m_addr_latch.addr = bitfield(data, 0, 7); - m_addr_latch.incr = bitfield(data, 7); -} - -void n163_core::data_w(u8 data, bool cpu_access) -{ - // 0x4800-0x4fff Sound data write - m_ram[m_addr_latch.addr] = data; - - // address latch increment - if (cpu_access && m_addr_latch.incr) - m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7); -} - -u8 n163_core::data_r(bool cpu_access) -{ - // 0x4800-0x4fff Sound data read - const u8 ret = m_ram[m_addr_latch.addr]; - - // address latch increment - if (cpu_access && m_addr_latch.incr) - m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7); - - return ret; -} diff --git a/src/engine/platform/sound/n163/n163.hpp b/src/engine/platform/sound/n163/n163.hpp deleted file mode 100644 index 317a33b45..000000000 --- a/src/engine/platform/sound/n163/n163.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Namco 163 Sound emulation core -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_N163_HPP -#define _VGSOUND_EMU_N163_HPP - -#pragma once - -namespace n163 -{ - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - typedef signed short s16; - - // get bitfield, bitfield(input, position, len) - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -}; - -using namespace n163; -class n163_core -{ -public: - // accessors, getters, setters - void addr_w(u8 data); - void data_w(u8 data, bool cpu_access = false); - u8 data_r(bool cpu_access = false); - - void set_disable(bool disable) { m_disable = disable; } - - // internal state - void reset(); - void tick(); - - // sound output pin - s16 out() { return m_out; } - - // get channel output - s16 chan_out(u8 ch) { return m_ch_out[ch]; } - - // get voice cycle - u8 voice_cycle() { return m_voice_cycle; } - - // register pool - u8 reg(u8 addr) { return m_ram[addr & 0x7f]; } - void set_multiplex(bool multiplex = true) { m_multiplex = multiplex; } - -private: - // Address latch - struct addr_latch_t - { - addr_latch_t() - : addr(0) - , incr(0) - { } - - void reset() - { - addr = 0; - incr = 0; - } - - u8 addr : 7; - u8 incr : 1; - }; - - bool m_disable = false; - u8 m_ram[0x80] = {0}; // internal 128 byte RAM - u8 m_voice_cycle = 0x78; // Voice cycle for processing - addr_latch_t m_addr_latch; // address latch - s16 m_out = 0; // output - s16 m_ch_out[8] = {0}; // per channel output - // demultiplex related - bool m_multiplex = true; // multiplex flag, but less noisy = inaccurate! - s16 m_acc = 0; // accumulated output -}; - -#endif diff --git a/src/engine/platform/sound/oki/msm6295.cpp b/src/engine/platform/sound/oki/msm6295.cpp deleted file mode 100644 index e7f39d27e..000000000 --- a/src/engine/platform/sound/oki/msm6295.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: tildearrow - OKI MSM6295 emulation core - - It is 4 channel ADPCM playback chip from OKI semiconductor. - It was becomes de facto standard for ADPCM playback in arcade machine, due to cost performance. - - The chip itself is pretty barebone: there is no "register" in chip. - It can't control volume and pitch in currently playing channels, only stopable them. - And volume is must be determined at playback start command. - - Command format: - - Playback command (2 byte): - - Byte Bit Description - 76543210 - 0 1xxxxxxx Phrase select (Header stored in ROM) - 1 x000---- Play channel 4 - 0x00---- Play channel 3 - 00x0---- Play channel 2 - 000x---- Play channel 1 - ----xxxx Volume - ----0000 0.0dB - ----0001 -3.2dB - ----0010 -6.0dB - ----0011 -9.2dB - ----0100 -12.0dB - ----0101 -14.5dB - ----0110 -18.0dB - ----0111 -20.5dB - ----1000 -24.0dB - - Suspend command (1 byte): - - Byte Bit Description - 76543210 - 0 0x------ Suspend channel 4 - 0-x----- Suspend channel 3 - 0--x---- Suspend channel 2 - 0---x--- Suspend channel 1 - - Frequency calculation: - if (SS) then - Frequency = Input clock / 165 - else then - Frequency = Input clock / 132 - -*/ - -#include "msm6295.hpp" - -#define CORE_DIVIDER 3 - -#define CHANNEL_DELAY (15/CORE_DIVIDER) -#define MASTER_DELAY (33/CORE_DIVIDER) - -void msm6295_core::tick() -{ - // command handler - if (m_command_pending) - { - if (bitfield(m_command, 7)) // play voice - { - if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4)))) - { - m_clock = 0; - if (bitfield(m_next_command, 4, 4) != 0) - { - for (int i = 0; i < 4; i++) - { - if (bitfield(m_next_command, 4 + i)) - { - if (!m_voice[i].m_busy) - { - m_voice[i].m_command = m_command; - m_voice[i].m_volume = (bitfield(m_next_command, 0, 4) < 9) ? m_volume_table[std::min(8, bitfield(m_next_command, 0, 4))] : 0; - } - break; // voices aren't be playable simultaneously at once - } - } - } - m_command = 0; - m_command_pending = false; - } - } - else if (bitfield(m_next_command, 7)) // select phrase - { - if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4)))) - { - m_clock = 0; - m_command = m_next_command; - m_command_pending = false; - } - } - else - { - if (bitfield(m_next_command, 3, 4) != 0) // suspend voices - { - for (int i = 0; i < 4; i++) - { - if (bitfield(m_next_command, 3 + i)) - { - if (m_voice[i].m_busy) - m_voice[i].m_command = m_next_command; - } - } - m_next_command &= ~0x78; - } - m_command_pending = false; - } - } - m_out = 0; - for (int i = 0; i < 4; i++) - { - m_voice[i].tick(); - if (!m_voice[i].m_muted) m_out += m_voice[i].m_out; - } -} - -void msm6295_core::reset() -{ - for (auto & elem : m_voice) - elem.reset(); - - m_command = 0; - m_next_command = 0; - m_command_pending = false; - m_clock = 0; - m_out = 0; -} - -void msm6295_core::voice_t::tick() -{ - if (!m_busy) - { - if (bitfield(m_command, 7)) - { - // get phrase header (stored in data memory) - const u32 phrase = bitfield(m_command, 0, 7) << 3; - // Start address - m_addr = (bitfield(m_host.m_intf.read_byte(phrase | 0), 0, 2) << 16) - | (m_host.m_intf.read_byte(phrase | 1) << 8) - | (m_host.m_intf.read_byte(phrase | 2) << 0); - // End address - m_end = (bitfield(m_host.m_intf.read_byte(phrase | 3), 0, 2) << 16) - | (m_host.m_intf.read_byte(phrase | 4) << 8) - | (m_host.m_intf.read_byte(phrase | 5) << 0); - m_nibble = 4; // MSB first, LSB second - m_command = 0; - m_busy = true; - vox_decoder_t::reset(); - } - m_out = 0; - } - else - { - // playback - if ((++m_clock) >= ((MASTER_DELAY * (m_host.m_ss ? 5 : 4)))) - { - m_clock = 0; - bool is_end = (m_command != 0); - m_curr.decode(bitfield(m_host.m_intf.read_byte(m_addr), m_nibble, 4)); - if (m_nibble <= 0) - { - m_nibble = 4; - if (++m_addr > m_end) - is_end = true; - } - else - m_nibble -= 4; - if (is_end) - { - m_command = 0; - m_busy = false; - } - m_out = (out() * m_volume) >> 7; // scale out to 12 bit output - } - } -} - -void msm6295_core::voice_t::reset() -{ - vox_decoder_t::reset(); - m_clock = 0; - m_busy = false; - m_command = 0; - m_addr = 0; - m_nibble = 0; - m_end = 0; - m_volume = 0; - m_out = 0; -} - -// accessors -u8 msm6295_core::busy_r() -{ - return (m_voice[0].m_busy ? 0x01 : 0x00) - | (m_voice[1].m_busy ? 0x02 : 0x00) - | (m_voice[2].m_busy ? 0x04 : 0x00) - | (m_voice[3].m_busy ? 0x08 : 0x00); -} - -void msm6295_core::command_w(u8 data) -{ - if (!m_command_pending) - { - m_next_command = data; - m_command_pending = true; - } -} diff --git a/src/engine/platform/sound/oki/msm6295.hpp b/src/engine/platform/sound/oki/msm6295.hpp deleted file mode 100644 index ca8b81d41..000000000 --- a/src/engine/platform/sound/oki/msm6295.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: tildearrow - OKI MSM6295 emulation core - - See msm6295.cpp for more info. -*/ - -#include "util.hpp" -#include "vox.hpp" -#include -#include - -#ifndef _VGSOUND_EMU_MSM6295_HPP -#define _VGSOUND_EMU_MSM6295_HPP - -#pragma once - -class msm6295_core : public vox_core -{ - friend class vgsound_emu_mem_intf; // common memory interface -public: - // constructor - msm6295_core(vgsound_emu_mem_intf &intf) - : m_voice{{*this,*this},{*this,*this},{*this,*this},{*this,*this}} - , m_intf(intf) - { - } - // accessors, getters, setters - u8 busy_r(); - void command_w(u8 data); - void ss_w(bool ss) { m_ss = ss; } // SS pin - - // internal state - void reset(); - void tick(); - - s32 out() { return m_out; } // built in 12 bit DAC - -private: - // Internal volume table, 9 step - const s32 m_volume_table[9] = { - 32/* 0.0dB */, - 22/* -3.2dB */, - 16/* -6.0dB */, - 11/* -9.2dB */, - 8/* -12.0dB */, - 6/* -14.5dB */, - 4/* -18.0dB */, - 3/* -20.5dB */, - 2/* -24.0dB */ }; // scale out to 5 bit for optimization - -public: - // msm6295 voice structs - struct voice_t : vox_decoder_t - { - // constructor - voice_t(vox_core &vox, msm6295_core &host) - : vox_decoder_t(vox) - , m_host(host) - {}; - - // internal state - virtual void reset() override; - void tick(); - - // accessors, getters, setters - // registers - msm6295_core &m_host; - u16 m_clock = 0; // clock counter - bool m_busy = false; // busy status - bool m_muted = false; // muted - u8 m_command = 0; // current command - u32 m_addr = 0; // current address - s8 m_nibble = 0; // current nibble - u32 m_end = 0; // end address - s32 m_volume = 0; // volume - s32 m_out = 0; // output - }; - voice_t m_voice[4]; -private: - vgsound_emu_mem_intf &m_intf; // common memory interface - - bool m_ss = false; // SS pin controls divider, input clock / 33 * (SS ? 5 : 4) - u8 m_command = 0; // Command byte - u8 m_next_command = 0; // Next command - bool m_command_pending = false; // command pending flag - u16 m_clock = 0; // clock counter - s32 m_out = 0; // 12 bit output -}; - -#endif diff --git a/src/engine/platform/sound/oki/util.hpp b/src/engine/platform/sound/oki/util.hpp deleted file mode 100644 index b9c50d7bf..000000000 --- a/src/engine/platform/sound/oki/util.hpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: tildearrow - Various core utilities for vgsound_emu -*/ - -#include -#include -#include - -#ifndef _VGSOUND_EMU_CORE_UTIL_HPP -#define _VGSOUND_EMU_CORE_UTIL_HPP - -#pragma once - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; -typedef signed char s8; -typedef signed short s16; -typedef signed int s32; -typedef signed long long s64; -typedef float f32; -typedef double f64; - -const f64 PI = 3.1415926535897932384626433832795; - -// get bitfield, bitfield(input, position, len) -template T bitfield(T in, u8 pos, u8 len = 1) -{ - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); -} - -// get sign extended value, sign_ext(input, len) -template T sign_ext(T in, u8 len) -{ - len = std::max(0, (8 * sizeof(T)) - len); - return T(T(in) << len) >> len; -} - -// convert attenuation decibel value to gain -inline f32 dB_to_gain(f32 attenuation) -{ - return powf(10.0f, attenuation / 20.0f); -} - -class vgsound_emu_mem_intf -{ -public: - virtual u8 read_byte(u32 address) { return 0; } - virtual u16 read_word(u32 address) { return 0; } - virtual u32 read_dword(u32 address) { return 0; } - virtual u64 read_qword(u32 address) { return 0; } - virtual void write_byte(u32 address, u8 data) { } - virtual void write_word(u32 address, u16 data) { } - virtual void write_dword(u32 address, u32 data) { } - virtual void write_qword(u32 address, u64 data) { } -}; - -template -struct clock_pulse_t -{ - void reset(T init = InitWidth) - { - m_edge.reset(); - m_width = m_width_latch = m_counter = init; - m_cycle = 0; - } - - bool tick(T width = 0) - { - bool carry = ((--m_counter) <= 0); - if (carry) - { - if (!width) - m_width = m_width_latch; - else - m_width = width; // reset width - m_counter = m_width; - m_cycle = 0; - } - else - m_cycle++; - - m_edge.tick(carry); - return carry; - } - - void set_width(T width) { m_width = width; } - void set_width_latch(T width) { m_width_latch = width; } - - // Accessors - bool current_edge() { return m_edge.m_current; } - bool rising_edge() { return m_edge.m_rising; } - bool falling_edge() { return m_edge.m_rising; } - T cycle() { return m_cycle; } - - struct edge_t - { - edge_t() - : m_current(InitEdge ^ 1) - , m_previous(InitEdge) - , m_rising(0) - , m_falling(0) - , m_changed(0) - { - set(InitEdge); - } - - void tick(bool toggle) - { - u8 current = m_current; - if (toggle) - current ^= 1; - set(current); - } - - void set(u8 edge) - { - edge &= 1; - m_rising = m_falling = m_changed = 0; - if (m_current != edge) - { - m_changed = 1; - if (m_current && (!edge)) - m_falling = 1; - else if ((!m_current) && edge) - m_rising = 1; - m_current = edge; - } - m_previous = m_current; - } - - void reset() - { - m_previous = InitEdge; - m_current = InitEdge ^ 1; - set(InitEdge); - } - - u8 m_current : 1; // current edge - u8 m_previous : 1; // previous edge - u8 m_rising : 1; // rising edge - u8 m_falling : 1; // falling edge - u8 m_changed : 1; // changed flag - }; - - edge_t m_edge; - T m_width = InitWidth; // clock pulse width - T m_width_latch = InitWidth; // clock pulse width latch - T m_counter = InitWidth; // clock counter - T m_cycle = 0; // clock cycle -}; - -#endif diff --git a/src/engine/platform/sound/oki/vox.hpp b/src/engine/platform/sound/oki/vox.hpp deleted file mode 100644 index 23fbfd78e..000000000 --- a/src/engine/platform/sound/oki/vox.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: tildearrow - Dialogic ADPCM core -*/ - -#include "util.hpp" -#include -#include - -#ifndef _VGSOUND_EMU_CORE_VOX_HPP -#define _VGSOUND_EMU_CORE_VOX_HPP - -#pragma once - -#define MODIFIED_CLAMP(x,xMin,xMax) (std::min(std::max((x),(xMin)),(xMax))) - -class vox_core -{ -protected: - struct vox_decoder_t - { - vox_decoder_t(vox_core &vox) - : m_curr(vox) - , m_loop(vox) - { }; - - virtual void reset() - { - m_curr.reset(); - m_loop.reset(); - m_loop_saved = false; - } - - void save() - { - if (!m_loop_saved) - { - m_loop.copy(m_curr); - m_loop_saved = true; - } - } - - void restore() - { - if (m_loop_saved) - m_curr.copy(m_loop); - } - - s32 out() { return m_curr.m_step; } - - struct decoder_state_t - { - decoder_state_t(vox_core &vox) - : m_vox(vox) - { }; - - void reset() - { - m_index = 0; - m_step = 16; - } - - void copy(decoder_state_t src) - { - m_index = src.m_index; - m_step = src.m_step; - } - - void decode(u8 nibble) - { - const u8 delta = bitfield(nibble, 0, 3); - s16 ss = m_vox.m_step_table[m_index]; // ss(n) - - // d(n) = (ss(n) * B2) + ((ss(n) / 2) * B1) + ((ss(n) / 4) * B0) + (ss(n) / 8) - s16 d = ss >> 3; - if (bitfield(delta, 2)) - d += ss; - if (bitfield(delta, 1)) - d += (ss >> 1); - if (bitfield(delta, 0)) - d += (ss >> 2); - - // if (B3 = 1) then d(n) = d(n) * (-1) X(n) = X(n-1) * d(n) - if (bitfield(nibble, 3)) - m_step = std::max(m_step - d, -2048); - else - m_step = std::min(m_step + d, 2047); - - // adjust step index - m_index = MODIFIED_CLAMP(m_index + m_vox.m_index_table[delta], 0, 48); - } - - vox_core &m_vox; - s8 m_index = 0; - s32 m_step = 16; - }; - - decoder_state_t m_curr; - decoder_state_t m_loop; - bool m_loop_saved = false; - }; - - s8 m_index_table[8] = {-1, -1, -1, -1, 2, 4, 6, 8}; - s32 m_step_table[49] = { - 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, - 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, - 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 - }; -}; - -#endif diff --git a/src/engine/platform/sound/scc/scc.cpp b/src/engine/platform/sound/scc/scc.cpp deleted file mode 100644 index e2ebcf200..000000000 --- a/src/engine/platform/sound/scc/scc.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst - Modifiers and Contributors for Furnace: Natt Akuma, tildearrow, Grauw - Konami SCC emulation core - - Konami SCC means "Sound Creative Chip", it's actually MSX MegaROM/RAM Mapper with 5 channel Wavetable sound generator. - - It was first appeared at 1987, F-1 Spirit and Nemesis 2/Gradius 2 for MSX. then several MSX cartridges used that until 1990, Metal Gear 2: Solid Snake. - Even after MSX is discontinued, it was still used at some low-end arcade and amusement hardwares. - and some Third-party MSX utilities still support this due to its market shares. - - There's 2 SCC types: - - K051649 (or simply known as SCC) - This chip is used for MSX MegaROM Mapper, some arcade machines. - Channel 4 and 5 must be share waveform, other channels has its own waveforms. - - K052539 (also known as SCC+) - This chip is used for MSX MegaRAM Mapper (Konami Sound Cartridges for Snatcher/SD Snatcher). - All channels can be has its own waveforms, and also has backward compatibility mode with K051649. - - Based on: - https://www.msx.org/wiki/MegaROM_Mappers - https://www.msx.org/wiki/Konami_051649 - https://www.msx.org/wiki/Konami_052539 - http://bifi.msxnet.org/msxnet/tech/scc - http://bifi.msxnet.org/msxnet/tech/soundcartridge - - K051649 Register Layout - - -------------------------------------------------------------------- - - 4000-bfff MegaROM Mapper - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - 4000-5fff xxxx xxxx R Bank page 0 - c000-dfff mirror of 4000-5fff - - 6000-7fff xxxx xxxx R Bank page 1 - e000-ffff mirror of 6000-7fff - - 8000-9fff xxxx xxxx R Bank page 2 - 0000-1fff mirror of 8000-9fff - - a000-bfff xxxx xxxx R Bank page 3 - 2000-3fff mirror of a000-bfff - - -------------------------------------------------------------------- - - 5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - 5000 --xx xxxx W Bank select, Page 0 - 5001-57ff Mirror of 5000 - - 7000 --xx xxxx W Bank select, Page 1 - 7001-77ff Mirror of 7000 - - 9000 --xx xxxx W Bank select, Page 2 - --11 1111 W SCC Enable - 9001-97ff Mirror of 9000 - - b000 --xx xxxx W Bank select, Page 3 - b001-b7ff Mirror of b000 - - -------------------------------------------------------------------- - - 9800-9fff SCC register - - -------------------------------------------------------------------- - - 9800-987f Waveform - - Address Bit R/W Description - 7654 3210 - - 9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) - 9820-983f xxxx xxxx R/W Channel 1 "" - 9840-985f xxxx xxxx R/W Channel 2 "" - 9860-987f xxxx xxxx R/W Channel 3/4 "" - - 9880-9889 Pitch - - 9880 xxxx xxxx W Channel 0 Pitch LSB - 9881 ---- xxxx W Channel 0 Pitch MSB - 9882 xxxx xxxx W Channel 1 Pitch LSB - 9883 ---- xxxx W Channel 1 Pitch MSB - 9884 xxxx xxxx W Channel 2 Pitch LSB - 9885 ---- xxxx W Channel 2 Pitch MSB - 9886 xxxx xxxx W Channel 3 Pitch LSB - 9887 ---- xxxx W Channel 3 Pitch MSB - 9888 xxxx xxxx W Channel 4 Pitch LSB - 9889 ---- xxxx W Channel 4 Pitch MSB - - 9888-988e Volume - - 988a ---- xxxx W Channel 0 Volume - 988b ---- xxxx W Channel 1 Volume - 988c ---- xxxx W Channel 2 Volume - 988d ---- xxxx W Channel 3 Volume - 988e ---- xxxx W Channel 4 Volume - - 988f ---x ---- W Channel 4 Output enable/disable flag - ---- x--- W Channel 3 Output enable/disable flag - ---- -x-- W Channel 2 Output enable/disable flag - ---- --x- W Channel 1 Output enable/disable flag - ---- ---x W Channel 0 Output enable/disable flag - - 9890-989f Mirror of 9880-988f - - 98a0-98bf xxxx xxxx R Channel 4 Waveform - - 98e0 x--- ---- W Waveform rotate flag for channel 4 - -x-- ---- W Waveform rotate flag for all channels - --x- ---- W Reset waveform position after pitch writes - ---- --x- W 8 bit frequency - ---- --0x W 4 bit frequency - - 98e1-98ff Mirror of 98e0 - - 9900-9fff Mirror of 9800-98ff - - -------------------------------------------------------------------- - - K052539 Register Layout - - -------------------------------------------------------------------- - - 4000-bfff MegaRAM Mapper - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - 4000-5fff xxxx xxxx R/W Bank page 0 - c000-dfff xxxx xxxx R/W "" - - 6000-7fff xxxx xxxx R/W Bank page 1 - e000-ffff xxxx xxxx R/W "" - - 8000-9fff xxxx xxxx R/W Bank page 2 - 0000-1fff xxxx xxxx R/W "" - - a000-bfff xxxx xxxx R/W Bank page 3 - 2000-3fff xxxx xxxx R/W "" - - -------------------------------------------------------------------- - - 5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - 5000 xxxx xxxx W Bank select, Page 0 - 5001-57ff Mirror of 5000 - - 7000 xxxx xxxx W Bank select, Page 1 - 7001-77ff Mirror of 7000 - - 9000 xxxx xxxx W Bank select, Page 2 - --11 1111 W SCC Enable (SCC Compatible mode) - 9001-97ff Mirror of 9000 - - b000 xxxx xxxx W Bank select, Page 3 - 1--- ---- W SCC+ Enable (SCC+ mode) - b001-b7ff Mirror of b000 - - -------------------------------------------------------------------- - - bffe-bfff Mapper configuration - - -------------------------------------------------------------------- - - Address Bit R/W Description - 7654 3210 - - bffe --x- ---- W SCC operation mode - --0- ---- W SCC Compatible mode - --1- ---- W SCC+ mode - ---x ---- W RAM write/Bank select toggle for all Bank pages - ---0 ---- W Bank select enable - ---1 ---- W RAM write enable - ---0 -x-- W RAM write/Bank select toggle for Bank page 2 - ---0 --x- W RAM write/Bank select toggle for Bank page 1 - ---0 ---x W RAM write/Bank select toggle for Bank page 0 - bfff Mirror of bffe - - -------------------------------------------------------------------- - - 9800-9fff SCC Compatible mode register - - -------------------------------------------------------------------- - - 9800-987f Waveform - - Address Bit R/W Description - 7654 3210 - - 9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) - 9820-983f xxxx xxxx R/W Channel 1 "" - 9840-985f xxxx xxxx R/W Channel 2 "" - 9860-987f xxxx xxxx R/W Channel 3/4 "" - - 9880-9889 Pitch - - 9880 xxxx xxxx W Channel 0 Pitch LSB - 9881 ---- xxxx W Channel 0 Pitch MSB - 9882 xxxx xxxx W Channel 1 Pitch LSB - 9883 ---- xxxx W Channel 1 Pitch MSB - 9884 xxxx xxxx W Channel 2 Pitch LSB - 9885 ---- xxxx W Channel 2 Pitch MSB - 9886 xxxx xxxx W Channel 3 Pitch LSB - 9887 ---- xxxx W Channel 3 Pitch MSB - 9888 xxxx xxxx W Channel 4 Pitch LSB - 9889 ---- xxxx W Channel 4 Pitch MSB - - 9888-988e Volume - - 988a ---- xxxx W Channel 0 Volume - 988b ---- xxxx W Channel 1 Volume - 988c ---- xxxx W Channel 2 Volume - 988d ---- xxxx W Channel 3 Volume - 988e ---- xxxx W Channel 4 Volume - - 988f ---x ---- W Channel 4 Output enable/disable flag - ---- x--- W Channel 3 Output enable/disable flag - ---- -x-- W Channel 2 Output enable/disable flag - ---- --x- W Channel 1 Output enable/disable flag - ---- ---x W Channel 0 Output enable/disable flag - - 9890-989f Mirror of 9880-988f - - 98a0-98bf xxxx xxxx R Channel 4 Waveform - - 98c0 -x-- ---- W Waveform rotate flag for all channels - --x- ---- W Reset waveform position after pitch writes - ---- --x- W 8 bit frequency - ---- --0x W 4 bit frequency - - 98c1-98df Mirror of 98c0 - - 9900-9fff Mirror of 9800-98ff - - -------------------------------------------------------------------- - - b800-bfff SCC+ mode register - - -------------------------------------------------------------------- - - b800-b89f Waveform - - Address Bit R/W Description - 7654 3210 - - b800-b81f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) - b820-b83f xxxx xxxx R/W Channel 1 "" - b840-b85f xxxx xxxx R/W Channel 2 "" - b860-b87f xxxx xxxx R/W Channel 3 "" - b880-b89f xxxx xxxx R/W Channel 3 "" - - b8a0-b8a9 Pitch - - b8a0 xxxx xxxx W Channel 0 Pitch LSB - b8a1 ---- xxxx W Channel 0 Pitch MSB - b8a2 xxxx xxxx W Channel 1 Pitch LSB - b8a3 ---- xxxx W Channel 1 Pitch MSB - b8a4 xxxx xxxx W Channel 2 Pitch LSB - b8a5 ---- xxxx W Channel 2 Pitch MSB - b8a6 xxxx xxxx W Channel 3 Pitch LSB - b8a7 ---- xxxx W Channel 3 Pitch MSB - b8a8 xxxx xxxx W Channel 4 Pitch LSB - b8a9 ---- xxxx W Channel 4 Pitch MSB - - b8a8-b8ae Volume - - b8aa ---- xxxx W Channel 0 Volume - b8ab ---- xxxx W Channel 1 Volume - b8ac ---- xxxx W Channel 2 Volume - b8ad ---- xxxx W Channel 3 Volume - b8ae ---- xxxx W Channel 4 Volume - - b8af ---x ---- W Channel 4 Output enable/disable flag - ---- x--- W Channel 3 Output enable/disable flag - ---- -x-- W Channel 2 Output enable/disable flag - ---- --x- W Channel 1 Output enable/disable flag - ---- ---x W Channel 0 Output enable/disable flag - - b8b0-b8bf Mirror of b8a0-b8af - - b8c0 -x-- ---- W Waveform rotate flag for all channels - --x- ---- W Reset waveform position after pitch writes - ---- --x- W 8 bit frequency - ---- --0x W 4 bit frequency - - b8c1-b8df Mirror of b8c0 - - b900-bfff Mirror of b800-b8ff - - -------------------------------------------------------------------- - - SCC Frequency calculation: - if 8 bit frequency then - Frequency = Input clock / ((bit 0 to 7 of Pitch input) + 1) - else if 4 bit frequency then - Frequency = Input clock / ((bit 8 to 11 of Pitch input) + 1) - else - Frequency = Input clock / (Pitch input + 1) - -*/ - -#include "scc.hpp" -#include - -// shared SCC features -void scc_core::tick() -{ - m_out = 0; - for (auto & elem : m_voice) - { - elem.tick(); - m_out += elem.out; - } -} - -void scc_core::voice_t::tick() -{ - if (pitch >= 9) // or voice is halted - { - // update counter - Post decrement - u16 temp = counter; - if (m_host.m_test.freq_4bit) // 4 bit frequency mode - { - counter = (counter & ~0x0ff) | (bitfield(bitfield(counter, 0, 8) - 1, 0, 8) << 0); - counter = (counter & ~0xf00) | (bitfield(bitfield(counter, 8, 4) - 1, 0, 4) << 8); - } - else - counter = bitfield(counter - 1, 0, 12); - - // handle counter carry - bool carry = m_host.m_test.freq_8bit ? (bitfield(temp, 0, 8) == 0) : - (m_host.m_test.freq_4bit ? (bitfield(temp, 8, 4) == 0) : - (bitfield(temp, 0, 12) == 0)); - if (carry) - { - addr = bitfield(addr + 1, 0, 5); - counter = pitch; - } - } - // get output - if (enable) - out = (wave[addr] * volume) >> 4; // scale to 11 bit digital output - else - out = 0; -} - -void scc_core::reset() -{ - for (auto & elem : m_voice) - elem.reset(); - - m_test.reset(); - m_out = 0; - memset(m_reg,0,sizeof(m_reg)); -} - -void scc_core::voice_t::reset() -{ - memset(wave,0,sizeof(wave)); - enable = false; - pitch = 0; - volume = 0; - addr = 0; - counter = 0; - out = 0; -} - -// SCC accessors -u8 scc_core::wave_r(bool is_sccplus, u8 address) -{ - u8 ret = 0xff; - const u8 voice = bitfield(address, 5, 3); - if (voice > 4) - return ret; - - u8 wave_addr = bitfield(address, 0, 5); - - if (m_test.rotate) // rotate flag - wave_addr = bitfield(wave_addr + m_voice[voice].addr, 0, 5); - - if (!is_sccplus) - { - if (voice == 3) // rotate voice 3~4 flag - { - if (m_test.rotate4 || m_test.rotate) // rotate flag - wave_addr = bitfield(bitfield(address, 0, 5) + m_voice[3 + m_test.rotate].addr, 0, 5); - } - } - ret = m_voice[voice].wave[wave_addr]; - - return ret; -} - -void scc_core::wave_w(bool is_sccplus, u8 address, u8 data) -{ - if (m_test.rotate) // write protected - return; - - const u8 voice = bitfield(address, 5, 3); - if (voice > 4) - return; - - const u8 wave_addr = bitfield(address, 0, 5); - - if (!is_sccplus) - { - if (((voice >= 3) && m_test.rotate4) || (voice >= 4)) // Ignore if write protected, or voice 4 - return; - if (voice >= 3) // voice 3, 4 shares waveform - { - m_voice[3].wave[wave_addr] = data; - m_voice[4].wave[wave_addr] = data; - } - else - m_voice[voice].wave[wave_addr] = data; - } - else - m_voice[voice].wave[wave_addr] = data; -} - -void scc_core::freq_vol_enable_w(u8 address, u8 data) -{ - const u8 voice_freq = bitfield(address, 1, 3); - const u8 voice_reg = bitfield(address, 0, 4); - // *0-*f Pitch, Volume, Enable - switch (voice_reg) - { - case 0x0: // 0x*0 Voice 0 Pitch LSB - case 0x2: // 0x*2 Voice 1 Pitch LSB - case 0x4: // 0x*4 Voice 2 Pitch LSB - case 0x6: // 0x*6 Voice 3 Pitch LSB - case 0x8: // 0x*8 Voice 4 Pitch LSB - if (m_test.resetpos) // Reset address - m_voice[voice_freq].addr = 0; - m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0x0ff) | data; - m_voice[voice_freq].counter = m_voice[voice_freq].pitch; - break; - case 0x1: // 0x*1 Voice 0 Pitch MSB - case 0x3: // 0x*3 Voice 1 Pitch MSB - case 0x5: // 0x*5 Voice 2 Pitch MSB - case 0x7: // 0x*7 Voice 3 Pitch MSB - case 0x9: // 0x*9 Voice 4 Pitch MSB - if (m_test.resetpos) // Reset address - m_voice[voice_freq].addr = 0; - m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0xf00) | (u16(bitfield(data, 0, 4)) << 8); - m_voice[voice_freq].counter = m_voice[voice_freq].pitch; - break; - case 0xa: // 0x*a Voice 0 Volume - case 0xb: // 0x*b Voice 1 Volume - case 0xc: // 0x*c Voice 2 Volume - case 0xd: // 0x*d Voice 3 Volume - case 0xe: // 0x*e Voice 4 Volume - m_voice[voice_reg - 0xa].volume = bitfield(data, 0, 4); - break; - case 0xf: // 0x*f Enable/Disable flag - m_voice[0].enable = bitfield(data, 0); - m_voice[1].enable = bitfield(data, 1); - m_voice[2].enable = bitfield(data, 2); - m_voice[3].enable = bitfield(data, 3); - m_voice[4].enable = bitfield(data, 4); - break; - } -} - -void k051649_scc_core::scc_w(bool is_sccplus, u8 address, u8 data) -{ - const u8 voice = bitfield(address, 5, 3); - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3/4 Waveform - wave_w(false, address, data); - break; - case 0b100: // 0x80-0x9f Pitch, Volume, Enable - freq_vol_enable_w(address, data); - break; - case 0b111: // 0xe0-0xff Test register - m_test.freq_4bit = bitfield(data, 0); - m_test.freq_8bit = bitfield(data, 1); - m_test.resetpos = bitfield(data, 5); - m_test.rotate = bitfield(data, 6); - m_test.rotate4 = bitfield(data, 7); - break; - } - m_reg[address] = data; -} - -void k052539_scc_core::scc_w(bool is_sccplus, u8 address, u8 data) -{ - const u8 voice = bitfield(address, 5, 3); - if (is_sccplus) - { - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3 Waveform - case 0b100: // 0x80-0x9f Voice 4 Waveform - wave_w(true, address, data); - break; - case 0b101: // 0xa0-0xbf Pitch, Volume, Enable - freq_vol_enable_w(address, data); - break; - case 0b110: // 0xc0-0xdf Test register - m_test.freq_4bit = bitfield(data, 0); - m_test.freq_8bit = bitfield(data, 1); - m_test.resetpos = bitfield(data, 5); - m_test.rotate = bitfield(data, 6); - break; - default: - break; - } - } - else - { - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3/4 Waveform - wave_w(false, address, data); - break; - case 0b100: // 0x80-0x9f Pitch, Volume, Enable - freq_vol_enable_w(address, data); - break; - case 0b110: // 0xc0-0xdf Test register - m_test.freq_4bit = bitfield(data, 0); - m_test.freq_8bit = bitfield(data, 1); - m_test.resetpos = bitfield(data, 5); - m_test.rotate = bitfield(data, 6); - break; - default: - break; - } - } - m_reg[address] = data; -} - -u8 k051649_scc_core::scc_r(bool is_sccplus, u8 address) -{ - const u8 voice = bitfield(address, 5, 3); - const u8 wave = bitfield(address, 0, 5); - u8 ret = 0xff; - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3 Waveform - case 0b101: // 0xa0-0xbf Voice 4 Waveform - ret = wave_r(false, (std::min(4, voice) << 5) | wave); - break; - } - return ret; -} - -u8 k052539_scc_core::scc_r(bool is_sccplus, u8 address) -{ - const u8 voice = bitfield(address, 5, 3); - const u8 wave = bitfield(address, 0, 5); - u8 ret = 0xff; - if (is_sccplus) - { - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3 Waveform - case 0b100: // 0x80-0x9f Voice 4 Waveform - ret = wave_r(true, address); - break; - } - } - else - { - switch (voice) - { - case 0b000: // 0x00-0x1f Voice 0 Waveform - case 0b001: // 0x20-0x3f Voice 1 Waveform - case 0b010: // 0x40-0x5f Voice 2 Waveform - case 0b011: // 0x60-0x7f Voice 3 Waveform - case 0b101: // 0xa0-0xbf Voice 4 Waveform - ret = wave_r(false, (std::min(4, voice) << 5) | wave); - break; - } - } - return ret; -} diff --git a/src/engine/platform/sound/scc/scc.hpp b/src/engine/platform/sound/scc/scc.hpp deleted file mode 100644 index 24a365ccf..000000000 --- a/src/engine/platform/sound/scc/scc.hpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst - Modifiers and Contributors for Furnace: Natt Akuma, tildearrow, Grauw - Konami SCC emulation core - - See scc.cpp for more info. -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_SCC_HPP -#define _VGSOUND_EMU_SCC_HPP - -#pragma once - -namespace scc -{ - typedef unsigned char u8; - typedef signed char s8; - typedef unsigned short u16; - typedef signed short s16; - typedef unsigned int u32; - typedef signed int s32; - - // get bitfield, bitfield(input, position, len) - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -} - -using namespace scc; -// shared for SCCs -class scc_core -{ -public: - // constructor - scc_core() - : m_voice{*this,*this,*this,*this,*this} - {}; - virtual ~scc_core(){}; - - // accessors - virtual u8 scc_r(bool is_sccplus, u8 address) = 0; - virtual void scc_w(bool is_sccplus, u8 address, u8 data) = 0; - - // internal state - virtual void reset(); - void tick(); - - // getters - s32 out() { return m_out; } // output to DA0...DA10 pin - s32 chan_out(u8 ch) { return m_voice[ch].out; } - u8 reg(u8 address) { return m_reg[address]; } - -protected: - // voice structs - struct voice_t - { - // constructor - voice_t(scc_core &host) : m_host(host) {}; - - // internal state - void reset(); - void tick(); - - // registers - scc_core &m_host; - s8 wave[32] = {0}; // internal waveform - bool enable = false; // output enable flag - u16 pitch = 0; // pitch - u8 volume = 0; // volume - u8 addr = 0; // waveform pointer - u16 counter = 0; // frequency counter - s32 out = 0; // current output - }; - voice_t m_voice[5]; // 5 voices - - // accessor - u8 wave_r(bool is_sccplus, u8 address); - void wave_w(bool is_sccplus, u8 address, u8 data); - void freq_vol_enable_w(u8 address, u8 data); - - struct test_t - { - // constructor - test_t() - : freq_4bit(0) - , freq_8bit(0) - , resetpos(0) - , rotate(0) - , rotate4(0) - { }; - - void reset() - { - freq_4bit = 0; - freq_8bit = 0; - resetpos = 0; - rotate = 0; - rotate4 = 0; - } - - u8 freq_4bit : 1; // 4 bit frequency - u8 freq_8bit : 1; // 8 bit frequency - u8 resetpos : 1; // reset counter after pitch writes - u8 rotate : 1; // rotate and write protect waveform for all channels - u8 rotate4 : 1; // same as above but for channel 4 only - }; - - test_t m_test; // test register - s32 m_out = 0; // output to DA0...10 - - u8 m_reg[256] = {0}; // register pool -}; - -// SCC core -class k051649_scc_core : public scc_core -{ -public: - // accessors - virtual u8 scc_r(bool is_sccplus, u8 address) override; - virtual void scc_w(bool is_sccplus, u8 address, u8 data) override; -}; - -class k052539_scc_core : public k051649_scc_core -{ -public: - // accessors - virtual u8 scc_r(bool is_sccplus, u8 address) override; - virtual void scc_w(bool is_sccplus, u8 address, u8 data) override; -}; - -#endif diff --git a/src/engine/platform/sound/vrcvi/vrcvi.cpp b/src/engine/platform/sound/vrcvi/vrcvi.cpp deleted file mode 100644 index a811c2f44..000000000 --- a/src/engine/platform/sound/vrcvi/vrcvi.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Konami VRC VI sound emulation core - - It's one of NES mapper with built-in sound chip, and also one of 2 Konami VRCs with this feature. (rest one has OPLL derivatives.) - - It's also DACless like other sound chip and mapper-with-sound manufactured by konami, - the Chips 6 bit digital sound output is needs converted to analog sound output when you it want to make some sounds, or send to sound mixer. - - Its are used for Akumajou Densetsu (Japan release of Castlevania III), Madara, Esper Dream 2. - - The chip is installed in 351951 PCB and 351949A PCB. - - 351951 PCB is used exclusivly for Akumajou Densetsu, Small board has VRC VI, PRG and CHR ROM. - - It's configuration also calls VRC6a, iNES mapper 024. - - 351949A PCB is for Last 2 titles with VRC VI, Bigger board has VRC VI, PRG and CHR ROM, and Battery Backed 8K x 8 bit SRAM. - - Additionally, It's PRG A0 and A1 bit to VRC VI input is swapped, compare to above. - - It's configuration also calls VRC6b, iNES mapper 026. - - The chip itself has 053328, 053329, 053330 Revision, but Its difference between revision is unknown. - - Like other mappers for NES, It has internal timer - Its timer can be sync with scanline like other Konami mapper in this era. - - Register layout (Sound and Timer only; 351951 PCB case, 351949A swaps xxx1 and xxx2): - - Address Bits Description - 7654 3210 - - 9000-9002 Pulse 1 - - 9000 x--- ---- Pulse 1 Duty ignore - -xxx ---- Pulse 1 Duty cycle - ---- xxxx Pulse 1 Volume - 9001 xxxx xxxx Pulse 1 Pitch bit 0-7 - 9002 x--- ---- Pulse 1 Enable - ---- xxxx Pulse 1 Pitch bit 8-11 - - 9003 Sound control - - 9003 ---- -x-- 4 bit Frequency mode - ---- -0x- 8 bit Frequency mode - ---- ---x Halt - - a000-a002 Pulse 2 - - a000 x--- ---- Pulse 2 Duty ignore - -xxx ---- Pulse 2 Duty cycle - ---- xxxx Pulse 2 Volume - a001 xxxx xxxx Pulse 2 Pitch bit 0-7 - a002 x--- ---- Pulse 2 Enable - ---- xxxx Pulse 2 Pitch bit 8-11 - - b000-b002 Sawtooth - - b000 --xx xxxx Sawtooth Accumulate Rate - b001 xxxx xxxx Sawtooth Pitch bit 0-7 - b002 x--- ---- Sawtooth Enable - ---- xxxx Sawtooth Pitch bit 8-11 - - f000-f002 IRQ Timer - - f000 xxxx xxxx IRQ Timer latch - f001 ---- -0-- Sync with scanline - ---- --x- Enable timer - ---- ---x Enable timer after IRQ Acknowledge - f002 ---- ---- IRQ Acknowledge - - Frequency calculations: - - if 4 bit Frequency Mode then - Frequency: Input clock / (bit 8 to 11 of Pitch + 1) - end else if 8 bit Frequency Mode then - Frequency: Input clock / (bit 4 to 11 of Pitch + 1) - end else then - Frequency: Input clock / (Pitch + 1) - end -*/ - -#include "vrcvi.hpp" -#include - -void vrcvi_core::tick() -{ - m_out = 0; - if (!m_control.m_halt) // Halt flag - { - // tick per each clock - int elemIndex=0; - for (auto & elem : m_pulse) - { - if (elem.tick()) { - m_out += elem.m_control.m_volume; // add 4 bit pulse output - m_ch_out[elemIndex]=elem.m_control.m_volume; - } else { - m_ch_out[elemIndex]=0; - } - elemIndex++; - } - if (m_sawtooth.tick()) { - m_out += bitfield(m_sawtooth.m_accum, 3, 5); // add 5 bit sawtooth output - m_ch_out[2]=bitfield(m_sawtooth.m_accum, 3, 5); - } else { - m_ch_out[2]=0; - } - } - if (m_timer.tick()) - m_timer.counter_tick(); -} - -void vrcvi_core::reset() -{ - for (auto & elem : m_pulse) - elem.reset(); - - m_sawtooth.reset(); - m_timer.reset(); - m_control.reset(); - m_out = 0; - memset(m_ch_out,0,sizeof(m_ch_out)); -} - -bool vrcvi_core::alu_t::tick() -{ - if (m_divider.m_enable) - { - const u16 temp = m_counter; - // post decrement - if (bitfield(m_host.m_control.m_shift, 1)) - { - m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); - m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); - } - else if (bitfield(m_host.m_control.m_shift, 0)) - { - m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4); - m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0); - } - else - m_counter = bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12); - - // carry handling - bool carry = bitfield(m_host.m_control.m_shift, 1) ? (bitfield(temp, 8, 4) == 0) : - (bitfield(m_host.m_control.m_shift, 0) ? (bitfield(temp, 4, 8) == 0) : - (bitfield(temp, 0, 12) == 0)); - if (carry) - m_counter = m_divider.m_divider; - - return carry; - } - return false; -} - -bool vrcvi_core::pulse_t::tick() -{ - if (!m_divider.m_enable) - return false; - - if (vrcvi_core::alu_t::tick()) - m_cycle = bitfield(m_cycle + 1, 0, 4); - - return m_control.m_mode ? true : ((m_cycle > m_control.m_duty) ? true : false); -} - -bool vrcvi_core::sawtooth_t::tick() -{ - if (!m_divider.m_enable) - return false; - - if (vrcvi_core::alu_t::tick()) - { - if (bitfield(m_cycle++, 0)) // Even step only - m_accum += m_rate; - if (m_cycle >= 14) // Reset accumulator at every 14 cycles - { - m_accum = 0; - m_cycle = 0; - } - } - return (m_accum == 0) ? false : true; -} - -void vrcvi_core::alu_t::reset() -{ - m_divider.reset(); - m_counter = 0; - m_cycle = 0; -} - -void vrcvi_core::pulse_t::reset() -{ - vrcvi_core::alu_t::reset(); - m_control.reset(); -} - -void vrcvi_core::sawtooth_t::reset() -{ - vrcvi_core::alu_t::reset(); - m_rate = 0; - m_accum = 0; -} - -bool vrcvi_core::timer_t::tick() -{ - if (m_timer_control.m_enable) - { - if (!m_timer_control.m_sync) // scanline sync mode - { - m_prescaler -= 3; - if (m_prescaler <= 0) - { - m_prescaler += 341; - return true; - } - } - } - return (m_timer_control.m_enable && m_timer_control.m_sync) ? true : false; -} - -void vrcvi_core::timer_t::counter_tick() -{ - if (bitfield(++m_counter, 0, 8) == 0) - { - m_counter = m_counter_latch; - irq_set(); - } -} - -void vrcvi_core::timer_t::reset() -{ - m_timer_control.reset(); - m_prescaler = 341; - m_counter = m_counter_latch = 0; - irq_clear(); -} - -// Accessors - -void vrcvi_core::alu_t::divider_t::write(bool msb, u8 data) -{ - if (msb) - { - m_divider = (m_divider & ~0xf00) | (bitfield(data, 0, 4) << 8); - m_enable = bitfield(data, 7); - } - else - m_divider = (m_divider & ~0x0ff) | data; -} - - -void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data) -{ - pulse_t &v = m_pulse[voice]; - switch (address) - { - case 0x00: // Control - 0x9000 (Pulse 1), 0xa000 (Pulse 2) - v.m_control.m_mode = bitfield(data, 7); - v.m_control.m_duty = bitfield(data, 4, 3); - v.m_control.m_volume = bitfield(data, 0, 4); - break; - case 0x01: // Pitch LSB - 0x9001/0x9002 (Pulse 1), 0xa001/0xa002 (Pulse 2) - v.m_divider.write(false, data); - break; - case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2) - v.m_divider.write(true, data); - if (!v.m_divider.m_enable) // Reset duty cycle - v.m_cycle = 0; - break; - } -} - -void vrcvi_core::saw_w(u8 address, u8 data) -{ - switch (address) - { - case 0x00: // Sawtooth Accumulate - 0xb000 - m_sawtooth.m_rate = bitfield(data, 0, 6); - break; - case 0x01: // Pitch LSB - 0xb001/0xb002 (Sawtooth) - m_sawtooth.m_divider.write(false, data); - break; - case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth) - m_sawtooth.m_divider.write(true, data); - if (!m_sawtooth.m_divider.m_enable) // Reset accumulator - m_sawtooth.m_accum = 0; - break; - } -} - -void vrcvi_core::timer_w(u8 address, u8 data) -{ - switch (address) - { - case 0x00: // Timer latch - 0xf000 - m_timer.m_counter_latch = data; - break; - case 0x01: // Timer control - 0xf001/0xf002 - m_timer.m_timer_control.m_sync = bitfield(data, 2); - m_timer.m_timer_control.m_enable = bitfield(data, 1); - m_timer.m_timer_control.m_enable_ack = bitfield(data, 0); - if (m_timer.m_timer_control.m_enable) - { - m_timer.m_counter = m_timer.m_counter_latch; - m_timer.m_prescaler = 341; - } - m_timer.irq_clear(); - break; - case 0x02: // IRQ Acknowledge - 0xf002/0xf001 - m_timer.irq_clear(); - m_timer.m_timer_control.m_enable = m_timer.m_timer_control.m_enable_ack; - break; - } -} - -void vrcvi_core::control_w(u8 data) -{ - // Global control - 0x9003 - m_control.m_halt = bitfield(data, 0); - m_control.m_shift = bitfield(data, 1, 2); -} diff --git a/src/engine/platform/sound/vrcvi/vrcvi.hpp b/src/engine/platform/sound/vrcvi/vrcvi.hpp deleted file mode 100644 index 4a80f7577..000000000 --- a/src/engine/platform/sound/vrcvi/vrcvi.hpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Konami VRC VI sound emulation core - - See vrcvi.cpp to more infos. -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_VRCVI_HPP -#define _VGSOUND_EMU_VRCVI_HPP - -#pragma once - -namespace vrcvi -{ - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - typedef signed char s8; - typedef signed short s16; - - // get bitfield, bitfield(input, position, len) - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -}; - -class vrcvi_intf -{ -public: - virtual void irq_w(bool irq) { } -}; - -using namespace vrcvi; -class vrcvi_core -{ -public: - friend class vrcvi_intf; - // constructor - vrcvi_core(vrcvi_intf &intf) - : m_pulse{*this,*this} - , m_sawtooth(*this) - , m_timer(*this) - , m_intf(intf) - { - } - // accessors, getters, setters - void pulse_w(u8 voice, u8 address, u8 data); - void saw_w(u8 address, u8 data); - void timer_w(u8 address, u8 data); - void control_w(u8 data); - - // internal state - void reset(); - void tick(); - - // 6 bit output - s8 out() { return m_out; } - // channel output - s16 chan_out(u8 ch) { return m_ch_out[ch]; } -private: - // Common ALU for sound channels - struct alu_t - { - alu_t(vrcvi_core &host) - : m_host(host) - { }; - - - virtual void reset(); - virtual bool tick(); - - struct divider_t - { - divider_t() - : m_divider(0) - , m_enable(0) - { }; - - void reset() - { - m_divider = 0; - m_enable = 0; - } - - void write(bool msb, u8 data); - - u16 m_divider : 12; // divider (pitch) - u16 m_enable : 1; // channel enable flag - }; - - vrcvi_core &m_host; - divider_t m_divider; - u16 m_counter = 0; // clock counter - u8 m_cycle = 0; // clock cycle - }; - - // 2 Pulse channels - struct pulse_t : alu_t - { - pulse_t(vrcvi_core &host) - : alu_t(host) - { }; - - virtual void reset() override; - virtual bool tick() override; - - // Control bits - struct pulse_control_t - { - pulse_control_t() - : m_mode(0) - , m_duty(0) - , m_volume(0) - { }; - - void reset() - { - m_mode = 0; - m_duty = 0; - m_volume = 0; - } - - u8 m_mode : 1; // duty toggle flag - u8 m_duty : 3; // 3 bit duty cycle - u8 m_volume : 4; // 4 bit volume - }; - - pulse_control_t m_control; - }; - - // 1 Sawtooth channel - struct sawtooth_t : alu_t - { - sawtooth_t(vrcvi_core &host) - : alu_t(host) - { }; - - virtual void reset() override; - virtual bool tick() override; - - u8 m_rate = 0; // sawtooth accumulate rate - u8 m_accum = 0; // sawtooth accumulator, high 5 bit is accumulated to output - }; - - // Internal timer - struct timer_t - { - timer_t(vrcvi_core &host) - : m_host(host) - { }; - - void reset(); - bool tick(); - void counter_tick(); - - // IRQ update - void update() { m_host.m_intf.irq_w(m_timer_control.m_irq_trigger); } - void irq_set() - { - if (!m_timer_control.m_irq_trigger) - { - m_timer_control.m_irq_trigger = 1; - update(); - } - } - void irq_clear() - { - if (m_timer_control.m_irq_trigger) - { - m_timer_control.m_irq_trigger = 0; - update(); - } - } - - // Control bits - struct timer_control_t - { - timer_control_t() - : m_irq_trigger(0) - , m_enable_ack(0) - , m_enable(0) - , m_sync(0) - { }; - - void reset() - { - m_irq_trigger = 0; - m_enable_ack = 0; - m_enable = 0; - m_sync = 0; - } - - u8 m_irq_trigger : 1; - u8 m_enable_ack : 1; - u8 m_enable : 1; - u8 m_sync : 1; - }; - - vrcvi_core &m_host; // host core - timer_control_t m_timer_control; // timer control bits - s16 m_prescaler = 341; // prescaler - u8 m_counter = 0; // clock counter - u8 m_counter_latch = 0; // clock counter latch - }; - - struct global_control_t - { - global_control_t() - : m_halt(0) - , m_shift(0) - { }; - - void reset() - { - m_halt = 0; - m_shift = 0; - } - - u8 m_halt : 1; // halt sound - u8 m_shift : 2; // 4/8 bit right shift - }; - - pulse_t m_pulse[2]; // 2 pulse channels - sawtooth_t m_sawtooth; // sawtooth channel - timer_t m_timer; // internal timer - global_control_t m_control; // control - - vrcvi_intf &m_intf; - - s8 m_out = 0; // 6 bit output - s8 m_ch_out[3] = {0}; // per-channel output -}; - -#endif diff --git a/src/engine/platform/sound/x1_010/x1_010.cpp b/src/engine/platform/sound/x1_010/x1_010.cpp deleted file mode 100644 index 6b0041bad..000000000 --- a/src/engine/platform/sound/x1_010/x1_010.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Seta/Allumer X1-010 Emulation core - - the chip has 16 voices, all voices can be switchable to Wavetable or PCM sample playback mode. - It has also 2 output channels, but no known hardware using this feature for stereo sound. - - Wavetable needs to paired with envelope, it's always enabled and similar as AY PSG's one - but its shape is stored at RAM. - - PCM volume is stored by each register. - - Both volume is 4bit per output. - - Everything except PCM sample is stored at paired 8 bit RAM. - - RAM layout (common case: Address bit 12 is swapped when RAM is shared with CPU) - - ----------------------------- - 0000...007f Voice Registers - - 0000...0007 Voice 0 Register - - Address Bits Description - 7654 3210 - 0 x--- ---- Frequency divider* - ---- -x-- Envelope one-shot mode - ---- --x- Sound format - ---- --0- PCM - ---- --1- Wavetable - ---- ---x Keyon/off - PCM case: - 1 xxxx xxxx Volume (Each nibble is for each output) - - 2 xxxx xxxx Frequency* - - 4 xxxx xxxx Start address / 4096 - - 5 xxxx xxxx 0x100 - (End address / 4096) - Wavetable case: - 1 ---x xxxx Wavetable data select - - 2 xxxx xxxx Frequency LSB* - 3 xxxx xxxx "" MSB - - 4 xxxx xxxx Envelope period (.10 fixed point, Low 8 bit) - - 5 ---x xxxx Envelope shape select (!= 0 : Reserved for Voice registers) - - 0008...000f Voice 1 Register - ... - 0078...007f Voice 15 Register - ----------------------------- - 0080...0fff Envelope shape data (Same as volume; Each nibble is for each output) - - 0080...00ff Envelope shape data 1 - 0100...017f Envelope shape data 2 - ... - 0f80...0fff Envelope shape data 31 - ----------------------------- - 1000...1fff Wavetable data - - 1000...107f Wavetable data 0 - 1080...10ff Wavetable data 1 - ... - 1f80...1fff Wavetable data 31 - ----------------------------- - - * Frequency is 4.4 fixed point for PCM, - 6.10 for Wavetable. - Frequency divider is higher precision or just right shift? - needs verification. -*/ - -#include "x1_010.hpp" - -void x1_010_core::tick() -{ - // reset output - m_out[0] = m_out[1] = 0; - for (int i = 0; i < 16; i++) - { - voice_t &v = m_voice[i]; - v.tick(); - m_out[0] += v.data * v.vol_out[0]; - m_out[1] += v.data * v.vol_out[1]; - } -} - -void x1_010_core::voice_t::tick() -{ - data = vol_out[0] = vol_out[1] = 0; - if (flag.keyon) - { - if (flag.wavetable) // Wavetable - { - // envelope, each nibble is for each output - u8 vol = m_host.m_envelope[(bitfield(end_envshape, 0, 5) << 7) | bitfield(env_acc, 10, 7)]; - vol_out[0] = bitfield(vol, 4, 4); - vol_out[1] = bitfield(vol, 0, 4); - env_acc += start_envfreq; - if (flag.env_oneshot && bitfield(env_acc, 17)) - flag.keyon = false; - else - env_acc = bitfield(env_acc, 0, 17); - // get wavetable data - data = m_host.m_wave[(bitfield(vol_wave, 0, 5) << 7) | bitfield(acc, 10, 7)]; - acc = bitfield(acc + (freq >> flag.div), 0, 17); - } - else // PCM sample - { - // volume register, each nibble is for each output - vol_out[0] = bitfield(vol_wave, 4, 4); - vol_out[1] = bitfield(vol_wave, 0, 4); - // get PCM sample - data = m_host.m_intf.read_byte(bitfield(acc, 4, 20)); - acc += bitfield(freq, 0, 8) >> flag.div; - if ((acc >> 16) > (0xff ^ end_envshape)) - flag.keyon = false; - } - } -} - -u8 x1_010_core::ram_r(u16 offset) -{ - if (offset & 0x1000) // wavetable data - return m_wave[offset & 0xfff]; - else if (offset & 0xf80) // envelope shape data - return m_envelope[offset & 0xfff]; - else // channel register - return m_voice[bitfield(offset, 3, 4)].reg_r(offset & 0x7); -} - -void x1_010_core::ram_w(u16 offset, u8 data) -{ - if (offset & 0x1000) // wavetable data - m_wave[offset & 0xfff] = data; - else if (offset & 0xf80) // envelope shape data - m_envelope[offset & 0xfff] = data; - else // channel register - m_voice[bitfield(offset, 3, 4)].reg_w(offset & 0x7, data); -} - -u8 x1_010_core::voice_t::reg_r(u8 offset) -{ - switch (offset & 0x7) - { - case 0x00: return (flag.div << 7) - | (flag.env_oneshot << 2) - | (flag.wavetable << 1) - | (flag.keyon << 0); - case 0x01: return vol_wave; - case 0x02: return bitfield(freq, 0, 8); - case 0x03: return bitfield(freq, 8, 8); - case 0x04: return start_envfreq; - case 0x05: return end_envshape; - default: break; - } - return 0; -} - -void x1_010_core::voice_t::reg_w(u8 offset, u8 data) -{ - switch (offset & 0x7) - { - case 0x00: - { - const bool prev_keyon = flag.keyon; - flag.div = bitfield(data, 7); - flag.env_oneshot = bitfield(data, 2); - flag.wavetable = bitfield(data, 1); - flag.keyon = bitfield(data, 0); - if (!prev_keyon && flag.keyon) // Key on - { - acc = flag.wavetable ? 0 : (u32(start_envfreq) << 16); - env_acc = 0; - } - break; - } - case 0x01: - vol_wave = data; - break; - case 0x02: - freq = (freq & 0xff00) | data; - break; - case 0x03: - freq = (freq & 0x00ff) | (u16(data) << 8); - break; - case 0x04: - start_envfreq = data; - break; - case 0x05: - end_envshape = data; - break; - default: - break; - } -} - -void x1_010_core::voice_t::reset() -{ - flag.reset(); - vol_wave = 0; - freq = 0; - start_envfreq = 0; - end_envshape = 0; - acc = 0; - env_acc = 0; - data = 0; - vol_out[0] = vol_out[1] = 0; -} - -void x1_010_core::reset() -{ - for (auto & elem : m_voice) - elem.reset(); - - std::fill_n(&m_envelope[0], 0x1000, 0); - std::fill_n(&m_wave[0], 0x1000, 0); - m_out[0] = m_out[1] = 0; -} diff --git a/src/engine/platform/sound/x1_010/x1_010.hpp b/src/engine/platform/sound/x1_010/x1_010.hpp deleted file mode 100644 index b533b66d8..000000000 --- a/src/engine/platform/sound/x1_010/x1_010.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - License: BSD-3-Clause - see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details - - Copyright holder(s): cam900 - Modifiers and Contributors for Furnace: cam900, tildearrow - Seta/Allumer X1-010 Emulation core - - See x1_010.cpp for more info. -*/ - -#include -#include - -#ifndef _VGSOUND_EMU_X1_010_HPP -#define _VGSOUND_EMU_X1_010_HPP - -#pragma once - -namespace x1_010 -{ - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - typedef signed char s8; - typedef signed int s32; - - template T bitfield(T in, u8 pos, u8 len = 1) - { - return (in >> pos) & (len ? (T(1 << len) - 1) : 1); - } -} - -using namespace x1_010; -class x1_010_mem_intf -{ -public: - virtual u8 read_byte(u32 address) { return 0; } -}; - -using namespace x1_010; -class x1_010_core -{ - friend class x1_010_mem_intf; -public: - // constructor - x1_010_core(x1_010_mem_intf &intf) - : m_voice{*this,*this,*this,*this, - *this,*this,*this,*this, - *this,*this,*this,*this, - *this,*this,*this,*this} - , m_intf(intf) - { - m_envelope = std::make_unique(0x1000); - m_wave = std::make_unique(0x1000); - - std::fill_n(&m_envelope[0], 0x1000, 0); - std::fill_n(&m_wave[0], 0x1000, 0); - } - - // register accessor - u8 ram_r(u16 offset); - void ram_w(u16 offset, u8 data); - - // getters - s32 output(u8 channel) { return m_out[channel & 1]; } - s32 chan_out(u8 channel) { return (m_voice[channel].data * (m_voice[channel].vol_out[0]+m_voice[channel].vol_out[1]))<<2; } - - // internal state - void reset(); - void tick(); - -private: - // 16 voices in chip - struct voice_t - { - // constructor - voice_t(x1_010_core &host) : m_host(host) {} - - // internal state - void reset(); - void tick(); - - // register accessor - u8 reg_r(u8 offset); - void reg_w(u8 offset, u8 data); - - // registers - x1_010_core &m_host; - struct flag_t - { - u8 div : 1; - u8 env_oneshot : 1; - u8 wavetable : 1; - u8 keyon : 1; - void reset() - { - div = 0; - env_oneshot = 0; - wavetable = 0; - keyon = 0; - } - flag_t() - : div(0) - , env_oneshot(0) - , wavetable(0) - , keyon(0) - { } - }; - flag_t flag; - u8 vol_wave = 0; - u16 freq = 0; - u8 start_envfreq = 0; - u8 end_envshape = 0; - - // internal registers - u32 acc = 0; - u32 env_acc = 0; - s8 data = 0; - u8 vol_out[2] = {0}; - }; - voice_t m_voice[16]; - - // RAM - std::unique_ptr m_envelope = nullptr; - std::unique_ptr m_wave = nullptr; - - // output data - s32 m_out[2] = {0}; - - x1_010_mem_intf &m_intf; -}; - -#endif diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 2500ce190..449ee1597 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -86,9 +86,10 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len // Oscilloscope buffer part if (++writeOscBuf>=32) { writeOscBuf=0; - for (int i=0; i<3; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.chan_out(i)<<10; + for (int i=0; i<2; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<10; } + oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<10; } // Command part @@ -195,7 +196,7 @@ void DivPlatformVRC6::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOff) { chWrite(i,2,0); - } else { + } else if (chan[i].active) { chWrite(i,1,chan[i].freq&0xff); chWrite(i,2,0x80|((chan[i].freq>>8)&0xf)); } diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index 608baa100..81c81016f 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -23,10 +23,10 @@ #include #include "../dispatch.h" #include "../macroInt.h" -#include "sound/vrcvi/vrcvi.hpp" +#include "vgsound_emu/src/vrcvi/vrcvi.hpp" -class DivPlatformVRC6: public DivDispatch { +class DivPlatformVRC6: public DivDispatch, public vrcvi_intf { struct Channel { int freq, baseFreq, pitch, pitch2, note; int dacPeriod, dacRate, dacOut; @@ -75,7 +75,6 @@ class DivPlatformVRC6: public DivDispatch { std::queue writes; unsigned char sampleBank; unsigned char writeOscBuf; - vrcvi_intf intf; vrcvi_core vrc6; unsigned char regPool[13]; @@ -101,7 +100,7 @@ class DivPlatformVRC6: public DivDispatch { const char** getRegisterSheet(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); - DivPlatformVRC6() : vrc6(intf) {}; + DivPlatformVRC6() : vrc6(*this) {}; ~DivPlatformVRC6(); }; diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 0f989ac9d..87c7a91c4 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -23,9 +23,9 @@ #include //#define rWrite(a,v) pendingWrites[a]=v; -#define rWrite(a,v) if (!skipRegisterWrites) { x1_010->ram_w(a,v); if (dumpWrites) { addWrite(a,v); } } +#define rWrite(a,v) if (!skipRegisterWrites) { x1_010.ram_w(a,v); if (dumpWrites) { addWrite(a,v); } } -#define chRead(c,a) x1_010->ram_r((c<<3)|(a&7)) +#define chRead(c,a) x1_010.ram_r((c<<3)|(a&7)) #define chWrite(c,a,v) rWrite((c<<3)|(a&7),v) #define waveWrite(c,a,v) rWrite(0x1000|(chan[c].waveBank<<11)|(c<<7)|(a&0x7f),(v-128)&0xff) #define envFill(c,a) rWrite(0x800|(c<<7)|(a&0x7f),(chan[c].lvol<<4)|chan[c].rvol) @@ -207,10 +207,10 @@ const char** DivPlatformX1_010::getRegisterSheet() { void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t len) { for (size_t h=start; htick(); + x1_010.tick(); - signed int tempL=x1_010->output(0); - signed int tempR=x1_010->output(1); + signed int tempL=x1_010.output(0); + signed int tempR=x1_010.output(1); if (tempL<-32768) tempL=-32768; if (tempL>32767) tempL=32767; @@ -222,11 +222,18 @@ void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t l bufR[h]=stereo?tempR:bufL[h]; for (int i=0; i<16; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=x1_010->chan_out(i); + oscBuf[i]->data[oscBuf[i]->needle++]=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))>>1; } } } +u8 DivPlatformX1_010::read_byte(u32 address) { + if ((sampleMem!=NULL) && (addressram_r(i); + regPool[i]=x1_010.ram_r(i); } return regPool; } @@ -829,7 +836,7 @@ void DivPlatformX1_010::reset() { chan[i].ws.setEngine(parent); chan[i].ws.init(NULL,128,255,false); } - x1_010->reset(); + x1_010.reset(); sampleBank=0; // set per-channel initial panning for (int i=0; i<16; i++) { @@ -942,9 +949,7 @@ int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned in setFlags(flags); sampleMem=new unsigned char[getSampleMemCapacity()]; sampleMemLen=0; - intf.memory=sampleMem; - x1_010=new x1_010_core(intf); - x1_010->reset(); + x1_010.reset(); reset(); return 16; } @@ -953,7 +958,6 @@ void DivPlatformX1_010::quit() { for (int i=0; i<16; i++) { delete oscBuf[i]; } - delete x1_010; delete[] sampleMem; } diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index 178a89383..84d1040ef 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -24,20 +24,9 @@ #include "../engine.h" #include "../macroInt.h" #include "../waveSynth.h" -#include "sound/x1_010/x1_010.hpp" +#include "vgsound_emu/src/x1_010/x1_010.hpp" -class DivX1_010Interface: public x1_010_mem_intf { - public: - unsigned char* memory; - int sampleBank; - virtual u8 read_byte(u32 address) override { - if (memory==NULL) return 0; - return memory[address & 0xfffff]; - } - DivX1_010Interface(): memory(NULL), sampleBank(0) {} -}; - -class DivPlatformX1_010: public DivDispatch { +class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf { struct Channel { struct Envelope { struct EnvFlag { @@ -118,14 +107,14 @@ class DivPlatformX1_010: public DivDispatch { unsigned char* sampleMem; size_t sampleMemLen; unsigned char sampleBank; - DivX1_010Interface intf; - x1_010_core* x1_010; + x1_010_core x1_010; unsigned char regPool[0x2000]; double NoteX1_010(int ch, int note); void updateWave(int ch); void updateEnvelope(int ch); friend void putDispatchChan(void*,int,int); public: + u8 read_byte(u32 address); void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); @@ -151,6 +140,10 @@ class DivPlatformX1_010: public DivDispatch { const char** getRegisterSheet(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); + DivPlatformX1_010(): + DivDispatch(), + vgsound_emu_mem_intf(), + x1_010(*this) {} ~DivPlatformX1_010(); }; diff --git a/src/main.cpp b/src/main.cpp index a18560926..885a4c49e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -183,7 +183,7 @@ TAParamResult pVersion(String) { printf("- reSID by Dag Lem (GPLv2)\n"); printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n"); printf("- Stella by Stella Team (GPLv2)\n"); - printf("- vgsound_emu (first version) by cam900 (BSD 3-clause)\n"); + printf("- vgsound_emu (second version) by cam900 (zlib)\n"); return TA_PARAM_QUIT; } From bf2ec8f1c4138f079277954501a8c3f7b694cc17 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 00:05:54 +0900 Subject: [PATCH 11/63] Temporary workaround for accidently removed libs --- extern/vgsound_emu-modified | 1 + vgsound_emu-modified/.clang-format | 154 ++++ vgsound_emu-modified/.gitignore | 18 + vgsound_emu-modified/CHANGELOG.md | 17 + vgsound_emu-modified/CMakeLists.txt | 161 ++++ vgsound_emu-modified/LICENSE | 19 + vgsound_emu-modified/MODIFIED.md | 7 + vgsound_emu-modified/README.md | 131 +++ .../vgsound_emu/src/core/util.hpp | 263 ++++++ .../vgsound_emu/src/core/vox/vox.cpp | 75 ++ .../vgsound_emu/src/core/vox/vox.hpp | 115 +++ .../vgsound_emu/src/es550x/README.md | 88 ++ .../vgsound_emu/src/es550x/es5504.cpp | 456 +++++++++ .../vgsound_emu/src/es550x/es5504.hpp | 117 +++ .../vgsound_emu/src/es550x/es5505.cpp | 652 +++++++++++++ .../vgsound_emu/src/es550x/es5505.hpp | 304 ++++++ .../vgsound_emu/src/es550x/es5506.cpp | 870 ++++++++++++++++++ .../vgsound_emu/src/es550x/es5506.hpp | 385 ++++++++ .../vgsound_emu/src/es550x/es550x.cpp | 34 + .../vgsound_emu/src/es550x/es550x.hpp | 610 ++++++++++++ .../vgsound_emu/src/es550x/es550x_alu.cpp | 131 +++ .../vgsound_emu/src/es550x/es550x_filter.cpp | 72 ++ .../vgsound_emu/src/k005289/README.md | 23 + .../vgsound_emu/src/k005289/k005289.cpp | 42 + .../vgsound_emu/src/k005289/k005289.hpp | 83 ++ .../vgsound_emu/src/k007232/README.md | 75 ++ .../vgsound_emu/src/k007232/k007232.cpp | 169 ++++ .../vgsound_emu/src/k007232/k007232.hpp | 115 +++ .../vgsound_emu/src/k053260/README.md | 109 +++ .../vgsound_emu/src/k053260/k053260.cpp | 290 ++++++ .../vgsound_emu/src/k053260/k053260.hpp | 262 ++++++ .../vgsound_emu/src/msm6295/README.md | 67 ++ .../vgsound_emu/src/msm6295/msm6295.cpp | 179 ++++ .../vgsound_emu/src/msm6295/msm6295.hpp | 142 +++ .../vgsound_emu/src/n163/README.md | 88 ++ .../vgsound_emu/src/n163/n163.cpp | 120 +++ .../vgsound_emu/src/n163/n163.hpp | 109 +++ .../vgsound_emu/src/scc/README.md | 314 +++++++ .../vgsound_emu/src/scc/scc.cpp | 461 ++++++++++ .../vgsound_emu/src/scc/scc.hpp | 320 +++++++ .../vgsound_emu/src/template/template.cpp | 33 + .../vgsound_emu/src/template/template.hpp | 88 ++ .../vgsound_emu/src/vrcvi/README.md | 97 ++ .../vgsound_emu/src/vrcvi/vrcvi.cpp | 260 ++++++ .../vgsound_emu/src/vrcvi/vrcvi.hpp | 407 ++++++++ .../vgsound_emu/src/x1_010/README.md | 97 ++ .../vgsound_emu/src/x1_010/x1_010.cpp | 163 ++++ .../vgsound_emu/src/x1_010/x1_010.hpp | 179 ++++ 48 files changed, 8972 insertions(+) create mode 160000 extern/vgsound_emu-modified create mode 100644 vgsound_emu-modified/.clang-format create mode 100644 vgsound_emu-modified/.gitignore create mode 100644 vgsound_emu-modified/CHANGELOG.md create mode 100644 vgsound_emu-modified/CMakeLists.txt create mode 100644 vgsound_emu-modified/LICENSE create mode 100644 vgsound_emu-modified/MODIFIED.md create mode 100644 vgsound_emu-modified/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/core/util.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k005289/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k007232/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k053260/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/msm6295/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/n163/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/scc/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/template/template.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/template/template.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/x1_010/README.md create mode 100644 vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp create mode 100644 vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp diff --git a/extern/vgsound_emu-modified b/extern/vgsound_emu-modified new file mode 160000 index 000000000..7b988a671 --- /dev/null +++ b/extern/vgsound_emu-modified @@ -0,0 +1 @@ +Subproject commit 7b988a6714ebf61e8a5fad5c9ccbda2b85853fe1 diff --git a/vgsound_emu-modified/.clang-format b/vgsound_emu-modified/.clang-format new file mode 100644 index 000000000..edce098df --- /dev/null +++ b/vgsound_emu-modified/.clang-format @@ -0,0 +1,154 @@ +# +# License: Zlib +# see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details +# +# Copyright holder(s): cam900 +# Clang Format setting for vgsound_emu +# +--- +BasedOnStyle: Microsoft +UseCRLF: true +IndentWidth: 4 +ColumnLimit: 0 +--- +Language: Proto +DisableFormat: true +--- +Language: TableGen +DisableFormat: true +--- +Language: TextProto +DisableFormat: true +--- +Language: Cpp +TabWidth: 4 +UseTab: Always +AccessModifierOffset: 4 +ColumnLimit: 100 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false + AlignCompound: true + PadOperators: true +AlignEscapedNewlines: Right +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: Both +BreakBeforeBraces: Allman +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma +BreakStringLiterals: false +CompactNamespaces: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +EmptyLineAfterAccessModifier: Leave +EmptyLineBeforeAccessModifier: Always +FixNamespaceComments: true +IncludeBlocks: Regroup +IndentAccessModifiers: true +IndentCaseBlocks: true +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentRequiresClause: true +IndentRequires: true +IndentWrappedFunctionNames: false +InsertBraces: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +PPIndentWidth: 1 +PackConstructorInitializers: Never +PointerAlignment: Right +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: Microsoft +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RequiresClausePosition: OwnLine +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 0 +SortIncludes: CaseSensitive +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: After +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: false + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: [] diff --git a/vgsound_emu-modified/.gitignore b/vgsound_emu-modified/.gitignore new file mode 100644 index 000000000..6a46037be --- /dev/null +++ b/vgsound_emu-modified/.gitignore @@ -0,0 +1,18 @@ +.vs/* +.vscode/* +node_modules/* +package.json +package-lock.json + +build/* +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps diff --git a/vgsound_emu-modified/CHANGELOG.md b/vgsound_emu-modified/CHANGELOG.md new file mode 100644 index 000000000..bf1e66771 --- /dev/null +++ b/vgsound_emu-modified/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelogs + +## Important changes + +### V 2.1.0 (2022-09-08) + +Move source folder into vgsound_emu folder +CMake support +Move each readmes into README.md each folders + +## Details + +See [here](https://gitlab.com/cam900/vgsound_emu/-/commits/main). + +### Previous changelogs + +See [here](https://github.com/cam900/vgsound_emu/commits/main). diff --git a/vgsound_emu-modified/CMakeLists.txt b/vgsound_emu-modified/CMakeLists.txt new file mode 100644 index 000000000..17e14291c --- /dev/null +++ b/vgsound_emu-modified/CMakeLists.txt @@ -0,0 +1,161 @@ +# +# License: Zlib +# see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details +# +# Copyright holder(s): cam900 +# CMake for vgsound_emu +# + +cmake_minimum_required(VERSION 3.0) +project(vgsound_emu + VERSION 2.1.0 + LANGUAGES CXX) + +option(VGSOUND_EMU_ES5504 "Use ES5504 core" ON) +option(VGSOUND_EMU_ES5505 "Use ES5505 core" ON) +option(VGSOUND_EMU_ES5506 "Use ES5506 core" ON) +option(VGSOUND_EMU_K005289 "Use K005289 core" ON) +option(VGSOUND_EMU_K007232 "Use K007232 core" ON) +option(VGSOUND_EMU_K053260 "Use K053260 core" ON) +option(VGSOUND_EMU_MSM6295 "Use MSM6295 core" ON) +option(VGSOUND_EMU_NAMCO_163 "Use Namco 163 core" ON) +option(VGSOUND_EMU_SCC "Use SCC core" ON) +option(VGSOUND_EMU_VRCVI "Use VRC VI core" ON) +option(VGSOUND_EMU_X1_010 "Use X1-010 core" ON) + +message(STATUS "Host: ${CMAKE_HOST_SYSTEM_NAME}, ${CMAKE_HOST_SYSTEM_PROCESSOR}") +message(STATUS "Target: ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}") +message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID}") +message(STATUS "CMake version: ${CMAKE_VERSION}") +message(STATUS "Generator: ${CMAKE_GENERATOR}") +message(STATUS "Extra generator: ${CMAKE_EXTRA_GENERATOR}") +message(STATUS "Make program: ${CMAKE_MAKE_PROGRAM}") + +set(CORE_SOURCE "") +set(EMU_SOURCE "") + +# Core functions +list(APPEND CORE_SOURCE + vgsound_emu/src/core/util.hpp +) + +# Dialogic ADPCM +if(VGSOUND_EMU_MSM6295) + list(APPEND CORE_SOURCE + vgsound_emu/src/core/vox/vox.cpp + vgsound_emu/src/core/vox/vox.hpp + ) + message(STATUS "Using Dialogic ADPCM core") +endif() + +# ES5504, ES5505, ES5506 +if(VGSOUND_EMU_ES5504 OR VGSOUND_EMU_ES5505 OR VGSOUND_EMU_ES5506) + list(APPEND EMU_SOURCE + vgsound_emu/src/es550x/es550x.hpp + + vgsound_emu/src/es550x/es550x.cpp + vgsound_emu/src/es550x/es550x_alu.cpp + vgsound_emu/src/es550x/es550x_filter.cpp + ) + + if(VGSOUND_EMU_ES5504) + list(APPEND EMU_SOURCE + vgsound_emu/src/es550x/es5504.hpp + vgsound_emu/src/es550x/es5504.cpp + ) + message(STATUS "Using ES5504 core") + endif() + + if(VGSOUND_EMU_ES5505) + list(APPEND EMU_SOURCE + vgsound_emu/src/es550x/es5505.hpp + vgsound_emu/src/es550x/es5505.cpp + ) + message(STATUS "Using ES5505 core") + endif() + + if(VGSOUND_EMU_ES5506) + list(APPEND EMU_SOURCE + vgsound_emu/src/es550x/es5506.hpp + vgsound_emu/src/es550x/es5506.cpp + ) + message(STATUS "Using ES5506 core") + endif() +endif() + +# K005289 +if(VGSOUND_EMU_K005289) + list(APPEND EMU_SOURCE + vgsound_emu/src/k005289/k005289.hpp + vgsound_emu/src/k005289/k005289.cpp + ) + message(STATUS "Using K005289 core") +endif() + +# K007232 +if(VGSOUND_EMU_K007232) + list(APPEND EMU_SOURCE + vgsound_emu/src/k007232/k007232.hpp + vgsound_emu/src/k007232/k007232.cpp + ) + message(STATUS "Using K007232 core") +endif() + +# K053260 +if(VGSOUND_EMU_K053260) + list(APPEND EMU_SOURCE + vgsound_emu/src/k053260/k053260.hpp + vgsound_emu/src/k053260/k053260.cpp + ) + message(STATUS "Using K053260 core") +endif() + +# MSM6295 +if(VGSOUND_EMU_MSM6295) + list(APPEND EMU_SOURCE + vgsound_emu/src/msm6295/msm6295.hpp + vgsound_emu/src/msm6295/msm6295.cpp + ) + message(STATUS "Using MSM6295 core") +endif() + +# Namco 163 +if(VGSOUND_EMU_NAMCO_163) + list(APPEND EMU_SOURCE + vgsound_emu/src/n163/n163.hpp + vgsound_emu/src/n163/n163.cpp + ) + message(STATUS "Using Namco 163 core") +endif() + +# SCC +if(VGSOUND_EMU_SCC) + list(APPEND EMU_SOURCE + vgsound_emu/src/scc/scc.hpp + vgsound_emu/src/scc/scc.cpp + ) + message(STATUS "Using SCC core") +endif() + +# VRC VI +if(VGSOUND_EMU_VRCVI) + list(APPEND EMU_SOURCE + vgsound_emu/src/vrcvi/vrcvi.hpp + vgsound_emu/src/vrcvi/vrcvi.cpp + ) + message(STATUS "Using VRC VI core") +endif() + +# X1-010 +if(VGSOUND_EMU_X1_010) + list(APPEND EMU_SOURCE + vgsound_emu/src/x1_010/x1_010.hpp + vgsound_emu/src/x1_010/x1_010.cpp + ) + message(STATUS "Using X1-010 core") +endif() + +add_library(vgsound_emu STATIC ${CORE_SOURCE} ${EMU_SOURCE}) +target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +# target_compile_options(vgsound_emu PRIVATE -Wall -Werror) \ No newline at end of file diff --git a/vgsound_emu-modified/LICENSE b/vgsound_emu-modified/LICENSE new file mode 100644 index 000000000..928144684 --- /dev/null +++ b/vgsound_emu-modified/LICENSE @@ -0,0 +1,19 @@ +zlib License + +(C) 2022-present cam900 and contributors + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/vgsound_emu-modified/MODIFIED.md b/vgsound_emu-modified/MODIFIED.md new file mode 100644 index 000000000..432d014f9 --- /dev/null +++ b/vgsound_emu-modified/MODIFIED.md @@ -0,0 +1,7 @@ +# modification disclaimer + +this is a modified version of vgsound_emu emulation core library tailored for Furnace. + +it should not and shall NOT be mistaken for the original, authentic or actual version and revision of the library. + +you can get original software from [here](https://gitlab.com/cam900/vgsound_emu/). diff --git a/vgsound_emu-modified/README.md b/vgsound_emu-modified/README.md new file mode 100644 index 000000000..b24e2888b --- /dev/null +++ b/vgsound_emu-modified/README.md @@ -0,0 +1,131 @@ +# vgsound_emu V2 (modified) + +This is a library of video game sound chip emulation cores. useful for emulators, chiptune trackers, or players. + +This is a modified version of vgsound_emu, tailored for Furnace. + +## Important + +License is now changed to zlib license in vgsound_emu V2, now you must notify your all modifications. + +but [vgsound_emu V1 (pre-V2)](https://gitlab.com/cam900/vgsound_emu/-/tree/V1) is still exists, and it's still distributed under [BSD-3-Clause license](https://spdx.org/licenses/BSD-3-Clause.html).([details](https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE)) + +## V2 revision changes + +formatting codes with clang-format, Encapsulation for Maintenance, Fix GCC 12, Change license to zlib license for notify modifications in derived works from this cores. + +## Changelog + +See [here](https://gitlab.com/cam900/vgsound_emu/-/blob/main/CHANGELOG.md). + +## License + +This software is distributed under [zlib License](https://spdx.org/licenses/Zlib.html), unlike [vgsound_emu V1](https://gitlab.com/cam900/vgsound_emu/-/tree/V1)([standard BSD-3-Clause license](https://spdx.org/licenses/BSD-3-Clause.html)([details](https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE))). +You must notify your modifications at all files you have modified! + +See [here](https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE) for details. + +## Folders + +- vgsound_emu: base folder + - src: source codes for emulation cores + - core: core files used in most of emulation cores + - vox: Dialogic ADPCM core + - es550x: Ensoniq ES5504, ES5505, ES5506 PCM sound chip families, 25/32 voices with 16/4 stereo/6 stereo output channels + - k005289: Konami K005289, 2 timers + - k007232: Konami K007232, 2 PCM channels + - k053260: Konami K053260, 4 PCM or ADPCM channels with CPU to CPU communication feature + - msm6295: OKI MSM6295, 4 ADPCM channels + - n163: Namco 163, NES Mapper with up to 8 Wavetable channels + - scc: Konami SCC, MSX Mappers with 5 Wavetable channels + - vrcvi: Konami VRC VI, NES Mapper with 2 Pulse channels and 1 Sawtooth channel + - x1_010: Seta/Allumer X1-010, 16 Wavetable/PCM channels + - template: Template for sound emulation core + +## Build instruction + +### dependencies + +- CMake +- git (for source repository management) +- MSVC or GCC or Clang (for compile) + +### Clone repository + +type the following on a terminal/console/shell/whatever: + +``` +git clone https://gitlab.com/cam900/vgsound_emu.git +cd vgsound_emu +``` + +### Compile + +#### MSVC + +type the following on a developer tools command prompt: + +``` +mkdir build +cd build +cmake .. +msbuild ALL_BUILD.vcxproj +``` + +#### MinGW, GCC, Clang with MakeFile + +type the following on a terminal/console/shell/whatever: + +``` +mkdir build +cd build +cmake .. +make +``` + +### CMake options + +To add an CMake option from the command line: ```cmake -D= ..``` +You can add multiple option with CMake. + +#### Available options + +| Options | Available Value | Default | Descriptions | +| :-: | :-: | :-: | :-: | +| VGSOUND_EMU_ES5504 | ON/OFF | ON | Use ES5504 core | +| VGSOUND_EMU_ES5505 | ON/OFF | ON | Use ES5505 core | +| VGSOUND_EMU_ES5506 | ON/OFF | ON | Use ES5506 core | +| VGSOUND_EMU_K005289 | ON/OFF | ON | Use K005289 core | +| VGSOUND_EMU_K007232 | ON/OFF | ON | Use K007232 core | +| VGSOUND_EMU_K053260 | ON/OFF | ON | Use K053260 core | +| VGSOUND_EMU_MSM6295 | ON/OFF | ON | Use MSM6295 core | +| VGSOUND_EMU_NAMCO_163 | ON/OFF | ON | Use Namco 163 core | +| VGSOUND_EMU_SCC | ON/OFF | ON | Use SCC core | +| VGSOUND_EMU_VRCVI | ON/OFF | ON | Use VRC VI core | +| VGSOUND_EMU_X1_010 | ON/OFF | ON | Use X1-010 core | + +### Link at another project + +Copy this repository as submodule first, type the following on a terminal/console/shell/whatever: + +``` +git submodule add https://gitlab.com/cam900/vgsound_emu.git +``` + +Then, Add following options at your CMakeLists.txt file. + +example: + +``` +add_subdirectory( [EXCLUDE_FROM_ALL]) +... +target_include_directories( SYSTEM PRIVATE ) +target_link_libraries( PRIVATE vgsound_emu) +``` + +## Contributors + +- [cam900](https://gitlab.com/cam900) +- [Natt Akuma](https://github.com/akumanatt) +- [James Alan Nguyen](https://github.com/djtuBIG-MaliceX) +- [Laurens Holst](https://github.com/Grauw) diff --git a/vgsound_emu-modified/vgsound_emu/src/core/util.hpp b/vgsound_emu-modified/vgsound_emu/src/core/util.hpp new file mode 100644 index 000000000..a6a9d6a29 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/core/util.hpp @@ -0,0 +1,263 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Various core utilities for vgsound_emu +*/ + +#ifndef _VGSOUND_EMU_SRC_CORE_UTIL_HPP +#define _VGSOUND_EMU_SRC_CORE_UTIL_HPP + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace vgsound_emu +{ + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + typedef unsigned long long u64; + typedef signed char s8; + typedef signed short s16; + typedef signed int s32; + typedef signed long long s64; + typedef float f32; + typedef double f64; + + class vgsound_emu_core + { + public: + // constructors + vgsound_emu_core(std::string tag) + : m_tag(tag) + { + } + + // getters + std::string tag() { return m_tag; } + + protected: + const f64 PI = 3.1415926535897932384626433832795; + + // std::clamp is only for C++17 or later; I use my own code + template + T clamp(T in, T min, T max) + { +#if defined(_HAS_CXX17) && _HAS_CXX17 + // just use std::clamp if C++17 or above + return std::clamp(in, min, max); +#else + // otherwise, use my own implementation of std::clamp + return std::min(std::max(in, min), max); +#endif + } + + // get bitfield, bitfield(input, position, len) + template + T bitfield(T in, u8 pos, u8 len = 1) + { + return (in >> pos) & (len ? (T(1 << len) - 1) : 1); + } + + // get sign extended value, sign_ext(input, len) + template + T sign_ext(T in, u8 len) + { + len = std::max(0, (8 * sizeof(T)) - len); + return T(T(in) << len) >> len; + } + + // convert attenuation decibel value to gain + inline f32 dB_to_gain(f32 attenuation) { return std::pow(10.0f, attenuation / 20.0f); } + + private: + std::string m_tag = ""; // core tags + }; + + class vgsound_emu_mem_intf : public vgsound_emu_core + { + public: + // constructor + vgsound_emu_mem_intf() + : vgsound_emu_core("mem_intf") + { + } + + virtual u8 read_byte(u32 address) { return 0; } + + virtual u16 read_word(u32 address) { return 0; } + + virtual u32 read_dword(u32 address) { return 0; } + + virtual u64 read_qword(u32 address) { return 0; } + + virtual void write_byte(u32 address, u8 data) {} + + virtual void write_word(u32 address, u16 data) {} + + virtual void write_dword(u32 address, u32 data) {} + + virtual void write_qword(u32 address, u64 data) {} + }; + + template + class clock_pulse_t : public vgsound_emu_core + { + private: + const T m_init_width = 1; + + class edge_t : public vgsound_emu_core + { + private: + const u8 m_init_edge = 1; + + public: + edge_t(u8 init_edge = 0) + : vgsound_emu_core("clock_pulse_edge") + , m_init_edge(init_edge) + , m_current(init_edge ^ 1) + , m_previous(init_edge) + , m_rising(0) + , m_falling(0) + , m_changed(0) + { + set(init_edge); + } + + // internal states + void reset() + { + m_previous = m_init_edge; + m_current = m_init_edge ^ 1; + set(m_init_edge); + } + + void tick(bool toggle) + { + u8 current = m_current; + if (toggle) + { + current ^= 1; + } + set(current); + } + + void set(u8 edge) + { + edge &= 1; + m_rising = m_falling = m_changed = 0; + if (m_current != edge) + { + m_changed = 1; + if (m_current && (!edge)) + { + m_falling = 1; + } + else if ((!m_current) && edge) + { + m_rising = 1; + } + m_current = edge; + } + m_previous = m_current; + } + + // getters + inline bool current() { return m_current; } + + inline bool rising() { return m_rising; } + + inline bool falling() { return m_falling; } + + inline bool changed() { return m_changed; } + + private: + u8 m_current : 1; // current edge + u8 m_previous : 1; // previous edge + u8 m_rising : 1; // rising edge + u8 m_falling : 1; // falling edge + u8 m_changed : 1; // changed flag + }; + + public: + clock_pulse_t(T init_width, u8 init_edge = 0) + : vgsound_emu_core("clock_pulse") + , m_init_width(init_width) + , m_edge(edge_t(init_edge & 1)) + , m_width(init_width) + , m_width_latch(init_width) + , m_counter(init_width) + , m_cycle(0) + { + } + + void reset(T init) + { + m_edge.reset(); + m_width = m_width_latch = m_counter = init; + m_cycle = 0; + } + + inline void reset() { reset(m_init_width); } + + bool tick(T width = 0) + { + bool carry = ((--m_counter) <= 0); + if (carry) + { + if (!width) + { + m_width = m_width_latch; + } + else + { + m_width = width; // reset width + } + m_counter = m_width; + m_cycle = 0; + } + else + { + m_cycle++; + } + + m_edge.tick(carry); + return carry; + } + + inline void set_width(T width) { m_width = width; } + + inline void set_width_latch(T width) { m_width_latch = width; } + + // Accessors + inline bool current_edge() { return m_edge.current(); } + + inline bool rising_edge() { return m_edge.rising(); } + + inline bool falling_edge() { return m_edge.falling(); } + + // getters + edge_t &edge() { return m_edge; } + + inline T cycle() { return m_cycle; } + + private: + edge_t m_edge; + T m_width = 1; // clock pulse width + T m_width_latch = 1; // clock pulse width latch + T m_counter = 1; // clock counter + T m_cycle = 0; // clock cycle + }; +}; // namespace vgsound_emu + +using namespace vgsound_emu; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp b/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp new file mode 100644 index 000000000..ef06c1511 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp @@ -0,0 +1,75 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Dialogic ADPCM core +*/ + +#include "vox.hpp" + +// reset decoder +void vox_core::vox_decoder_t::decoder_state_t::reset() +{ + m_index = 0; + m_step = 16; +} + +// copy from source +void vox_core::vox_decoder_t::decoder_state_t::copy(decoder_state_t src) +{ + m_index = src.index(); + m_step = src.step(); +} + +// decode single nibble +void vox_core::vox_decoder_t::decoder_state_t::decode(u8 nibble) +{ + const u8 delta = bitfield(nibble, 0, 3); + const s16 ss = m_vox.m_step_table[m_index]; // ss(n) + + // d(n) = (ss(n) * B2) + ((ss(n) / 2) * B1) + ((ss(n) / 4) * B0) + // + (ss(n) / 8) + s16 d = ss >> 3; + if (bitfield(delta, 2)) + { + d += ss; + } + if (bitfield(delta, 1)) + { + d += (ss >> 1); + } + if (bitfield(delta, 0)) + { + d += (ss >> 2); + } + + // if (B3 = 1) then d(n) = d(n) * (-1) X(n) = X(n-1) * d(n) + if (bitfield(nibble, 3)) + { + m_step -= d; + } + else + { + m_step += d; + } + + if (m_wraparound) // wraparound (MSM5205) + { + if (m_step < -2048) + { + m_step &= 0x7ff; + } + else if (m_step > 2047) + { + m_step |= ~0x7ff; + } + } + else + { + m_step = clamp(m_step, -2048, 2047); + } + + // adjust step index + m_index = clamp(m_index + m_vox.m_index_table[delta], 0, 48); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp new file mode 100644 index 000000000..f3ae6d4c1 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp @@ -0,0 +1,115 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Dialogic ADPCM core +*/ + +#ifndef _VGSOUND_EMU_SRC_CORE_VOX_VOX_HPP +#define _VGSOUND_EMU_SRC_CORE_VOX_VOX_HPP + +#pragma once + +#include "../util.hpp" + +class vox_core : public vgsound_emu_core +{ + protected: + class vox_decoder_t : public vgsound_emu_core + { + private: + class decoder_state_t : public vgsound_emu_core + { + public: + decoder_state_t(vox_core &vox, bool wraparound) + : vgsound_emu_core("vox_decoder_state") + , m_wraparound(wraparound) + , m_vox(vox) + , m_index(0) + , m_step(16) + { + } + + // internal states + void reset(); + void decode(u8 nibble); + + // getters + s8 index() { return m_index; } + + s32 step() { return m_step; } + + decoder_state_t &operator=(decoder_state_t src) + { + copy(src); + return *this; + } + + private: + const bool m_wraparound = false; // wraparound or clamp? + + void copy(decoder_state_t src); + + vox_core &m_vox; + s8 m_index = 0; + s32 m_step = 16; + }; + + public: + vox_decoder_t(vox_core &vox, bool wraparound) + : vgsound_emu_core("vox_decoder") + , m_curr(vox, wraparound) + , m_loop(vox, wraparound) + , m_loop_saved(false) + { + } + + virtual void reset() + { + m_curr.reset(); + m_loop.reset(); + m_loop_saved = false; + } + + void save() + { + if (!m_loop_saved) + { + m_loop = m_curr; + m_loop_saved = true; + } + } + + void restore() + { + if (m_loop_saved) + { + m_curr = m_loop; + } + } + + void decode(u8 nibble) { m_curr.decode(nibble); } + + s32 step() { return m_curr.step(); } + + private: + decoder_state_t m_curr; + decoder_state_t m_loop; + bool m_loop_saved = false; + }; + + const s8 m_index_table[8] = {-1, -1, -1, -1, 2, 4, 6, 8}; + const s32 m_step_table[49] = { + 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, + 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, + 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552}; + + public: + vox_core(std::string tag) + : vgsound_emu_core(tag) + { + } +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/README.md b/vgsound_emu-modified/vgsound_emu/src/es550x/README.md new file mode 100644 index 000000000..6f410e7d4 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/README.md @@ -0,0 +1,88 @@ +# Ensoniq ES5504 DOCII, ES5505 OTIS, ES5506 OTTO + +## Summary + +- 32 voice of PCMs + - per-voice features: + - 4 pole, 4 mode filter (2 filter coefficient, 12 bit per them) + - max 16 output, 12 bit volume (ES5504) + - max 4 stereo output channels, 8 bit logarithmic volume per left/right output (ES5505) + - max 6 stereo output channels, 12 bit logarithmic volume per left/right output (ES5506) + - Hardware envelope for volume and filter (ES5506) + - 16 bit linear PCM + - 8 bit compressed PCM (ES5506) + - total accessible memory: 1 MWord (2MByte) per bank (ES5504, ES5505) or 2 MWord (4MByte) per bank (ES5506) + - CA flag is also usable for bank + - 1 bit BS flag for bank - 2 bank total (ES5505) + - 2 bit BS flag for bank - 4 bank total (ES5506) + +## Source code + +- es550x.hpp: Base header + - es550x.cpp: Emulation core for common shared features + - es550x_alu.cpp: Emulation core for shared ALU function + - es550x_filter.cpp: Emulation core for shared filter function + - es5504.hpp: ES5504 header + - es5504.cpp: Emulation core for ES5504 specific features + - es5505.hpp: ES5505 header + - es5505.cpp: Emulation core for ES5505 specific features + - es5506.hpp: ES5506 header + - es5506.cpp: Emulation core for ES5506 specific features + +## Description + +After ES5503 DOC's appeared, Ensoniq announces ES5504 DOC II, ES5505 OTIS, ES5506 OTTO. + +These are not just PCM chip; but with built-in 4 pole filters and variable voice limits. + +It can be trades higher sample rate and finer frequency and Tons of voices, or vice-versa. + +These are mainly used with their synthesizers, musical stuffs. It's also mainly paired with ES5510 ESP/ES5511 ESP2 for post processing. ES5506 can be paired with itself, It's called Dual chip configuration and Both chips are can be shares same memory spaces. + +ES5505 was also mainly used on Taito's early- to late-90s arcade hardware for their PCM sample based sound system, paired with ES5510 ESP for post processing. It's configuration is borrowed from Ensoniq's 32 Voice synths powered by these chips. It's difference is external logic to adds per-voice bankswitching looks like what Konami doing on K007232. + +Atari Panther was will be use ES5505, but finally canceled. + +Ensoniq's ISA Sound Card for PC, Soundscape used ES5506, "Elite" model has optional daughterboard with ES5510 for digital effects. + +## Related chips + +- ES5530 "OPUS" variant is 2-in-one chip with built-in ES5506 and Sequoia. + +- ES5540 "OTTOFX" variant is ES5506 and ES5510 merged in single package. + +- ES5548 "OTTO48" variant is used at late-90s ensoniq synths and musical instruments, 2 ES5506s are merged in single package, or with 48 voices in chip? + +## Chip difference + +### ES5504 to ES5505 + +- Total voice amount is expanded to 32, rather than 25. + +- ADC and DAC is completely redesigned. + +- it's has now voice-independent 10 bit and Sony/Burr-Brown format DAC. + +- Output channel and Volume is changed to 16 mono to 4 stereo, 12 bit Analog to 8 bit Stereo digital, also Floating point-ish format and independent per left and right output. + +- Channel 3 is can be Input/Output. + +- Channel output is can be accessible at host for test purpose. + +- Max sample memory is expanded to 2MWords (1MWords * 2 Banks) + +### ES5505 to ES5506 + +- Frequency is more finer now: 11 bit fraction rather than 9 bit. + +- Output channel and Volume is changed to 4 stereo to 6 stereo, 8 bit to 16 bit, but only 12 bit is used for calculation; 4 LSB is used for envelope ramping. + +- Transwave flag is added - its helpful for transwave process, with interrupt per voices. Hardware envelope is added - K1, K2, Volume value is can be modified in run-time. also K1, K2 is expanded to 16 bit for finer envelope ramping. + +- Filter calculation resolution is expanded to 18 bit. + +- All channels are output, Serial output is now partially programmable. + +- Max sample memory is expanded to 8MWords (2MWords * 4 Banks) + +- Register format between these chips are incompatible. diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp new file mode 100644 index 000000000..586f15f30 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp @@ -0,0 +1,456 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504 emulation core +*/ + +#include "es5504.hpp" + +// Internal functions +void es5504_core::tick() +{ + m_voice_update = false; + m_voice_end = false; + // /CAS, E + if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock + { + // /CAS + if (m_cas.tick()) + { + // /CAS high, E low: get sample address + if (m_cas.falling_edge()) + { + // /CAS low, E low: fetch sample + if (!m_e.current_edge()) + { + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + } + // E + if (m_clkin.falling_edge()) // falling edge triggers E clock + { + if (m_e.tick()) + { + m_intf.e_pin(m_e.current_edge()); + if (m_e.rising_edge()) // Host access + { + m_host_intf.update_strobe(); + voice_tick(); + } + if (m_e.falling_edge()) // Voice memory + { + m_host_intf.clear_host_access(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + if (m_e.current_edge()) // Host interface + { + if (m_host_intf.host_access()) + { + if (m_host_intf.rw() && (m_e.cycle() == 2)) // Read + { + m_hd = read(m_ha); + m_host_intf.clear_host_access(); + } + else if ((!m_host_intf.rw()) && (m_e.cycle() == 2)) + { // Write + write(m_ha, m_hd); + } + } + } + else if (!m_e.current_edge()) + { + if (m_e.cycle() == 2) + { + // reset host access state + m_hd = 0; + m_host_intf.clear_strobe(); + } + } + } + } +} + +// less cycle accurate, but less CPU heavy routine +void es5504_core::tick_perf() +{ + m_voice_update = false; + m_voice_end = false; + // update + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); +} + +void es5504_core::voice_tick() +{ + // Voice updates every 2 E clock cycle (= 1 CHSTRB cycle or 4 BCLK clock cycle) + m_voice_update = bitfield(m_voice_fetch++, 0); + if (m_voice_update) + { + // Update voice + m_voice[m_voice_cycle].tick(m_voice_cycle); + + // Refresh output (Multiplexed analog output) + m_out[m_voice[m_voice_cycle].cr().ca()] = m_voice[m_voice_cycle].out(); + + if ((++m_voice_cycle) > std::min(24, m_active)) // ~ 25 voices + { + m_voice_end = true; + m_voice_cycle = 0; + } + + m_voice_fetch = 0; + } +} + +void es5504_core::voice_t::fetch(u8 voice, u8 cycle) +{ + m_alu.set_sample( + cycle, + m_host.m_intf.read_sample(voice, + bitfield(m_cr.ca(), 0, 3), + bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer))); +} + +void es5504_core::voice_t::tick(u8 voice) +{ + m_out = 0; + + // Filter execute + m_filter.tick(m_alu.interpolation()); + + if (m_alu.busy()) + { + // Send to output + m_out = ((sign_ext(m_filter.o4_1(), 16) >> 3) * m_volume) >> + 12; // Analog multiplied in real chip, 13/12 bit ladder DAC + + // ALU execute + if (m_alu.tick()) + { + m_alu.loop_exec(); + } + + // ADC check + adc_exec(); + } + + // Update IRQ + m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice); +} + +// ADC; Correct? +void es5504_core::voice_t::adc_exec() +{ + if (m_cr.adc()) + { + m_host.m_adc = m_host.m_intf.adc_r() & ~0x7; + } +} + +void es5504_core::reset() +{ + es550x_shared_core::reset(); + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_adc = 0; + std::fill(m_out.begin(), m_out.end(), 0); +} + +void es5504_core::voice_t::reset() +{ + es550x_shared_core::es550x_voice_t::reset(); + m_volume = 0; + m_out = 0; +} + +// Accessors +u16 es5504_core::host_r(u8 address) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + if (m_e.rising_edge()) + { // update directly + m_hd = read(m_ha, true); + } + else + { + m_host_intf.set_strobe(true); + } + } + return m_hd; +} + +void es5504_core::host_w(u8 address, u16 data) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + m_hd = data; + if (m_e.rising_edge()) + { // update directly + write(m_ha, m_hd, true); + } + else + { + m_host_intf.set_strobe(false); + } + } +} + +u16 es5504_core::read(u8 address, bool cpu_access) { return regs_r(m_page, address, cpu_access); } + +void es5504_core::write(u8 address, u16 data, bool cpu_access) +{ + regs_w(m_page, address, data, cpu_access); +} + +u16 es5504_core::regs_r(u8 page, u8 address, bool cpu_access) +{ + u16 ret = 0xffff; + address = bitfield(address, 0, 4); // 4 bit address for CPU access + + if (address >= 12) // Global registers + { + switch (address) + { + case 12: // A/D (A to D Convert/Test) + ret = (ret & ~0xfffb) | (m_adc & 0xfffb); + break; + case 13: // ACT (Number of voices) + ret = (ret & ~0x1f) | bitfield(m_active, 0, 5); + break; + case 14: // IRQV (Interrupting voice vector) + ret = (ret & ~0x9f) | m_irqv.get(); + if (cpu_access) + { + m_irqv.clear(); + if (bool(bitfield(ret, 7)) != m_irqv.irqb()) + { + m_voice[m_irqv.voice()].alu().irq_update(m_intf, m_irqv); + } + } + break; + case 15: // PAGE (Page select register) + ret = (ret & ~0x3f) | bitfield(m_page, 0, 6); + break; + } + } + else // Voice specific registers + { + const u8 voice = bitfield(page, 0, 5); // Voice select + if (voice < 25) + { + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 56 + { + switch (address) + { + case 1: // O4(n-1) (Filter 4 Temp Register) + ret = v.filter().o4_1(); + break; + case 2: // O3(n-2) (Filter 3 Temp Register #2) + ret = v.filter().o3_2(); + break; + case 3: // O3(n-1) (Filter 3 Temp Register #1) + ret = v.filter().o3_1(); + break; + case 4: // O2(n-2) (Filter 2 Temp Register #2) + ret = v.filter().o2_2(); + break; + case 5: // O2(n-1) (Filter 2 Temp Register #1) + ret = v.filter().o2_1(); + break; + case 6: // O1(n-1) (Filter 1 Temp Register) + ret = v.filter().o1_1(); + break; + } + } + else // Page 0 - 24 + { + switch (address) + { + case 0: // CR (Control Register) + ret = (ret & ~0xff) | (v.alu().stop() ? 0x01 : 0x00) | + (v.cr().adc() ? 0x04 : 0x00) | (v.alu().lpe() ? 0x08 : 0x00) | + (v.alu().ble() ? 0x10 : 0x00) | (v.alu().irqe() ? 0x20 : 0x00) | + (v.alu().dir() ? 0x40 : 0x00) | (v.alu().irq() ? 0x80 : 0x00); + break; + case 1: // FC (Frequency Control) + ret = (ret & ~0xfffe) | (v.alu().fc() << 1); + break; + case 2: // STRT-H (Loop Start Register High) + ret = (ret & ~0x1fff) | bitfield(v.alu().start(), 16, 13); + break; + case 3: // STRT-L (Loop Start Register Low) + ret = (ret & ~0xffe0) | (v.alu().start() & 0xffe0); + break; + case 4: // END-H (Loop End Register High) + ret = (ret & ~0x1fff) | bitfield(v.alu().end(), 16, 13); + break; + case 5: // END-L (Loop End Register Low) + ret = (ret & ~0xffe0) | (v.alu().end() & 0xffe0); + break; + case 6: // K2 (Filter Cutoff Coefficient #2) + ret = (ret & ~0xfff0) | (v.filter().k2() & 0xfff0); + break; + case 7: // K1 (Filter Cutoff Coefficient #1) + ret = (ret & ~0xfff0) | (v.filter().k1() & 0xfff0); + break; + case 8: // Volume + ret = (ret & ~0xfff0) | ((v.volume() << 4) & 0xfff0); + break; + case 9: // CA (Filter Config, Channel Assign) + ret = (ret & ~0x3f) | bitfield(v.cr().ca(), 0, 4) | + (bitfield(v.filter().lp(), 0, 2) << 4); + break; + case 10: // ACCH (Accumulator High) + ret = (ret & ~0x1fff) | bitfield(v.alu().accum(), 16, 13); + break; + case 11: // ACCL (Accumulator Low) + ret = bitfield(v.alu().accum(), 0, 16); + break; + } + } + } + } + + return ret; +} + +void es5504_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access) +{ + address = bitfield(address, 0, 4); // 4 bit address for CPU access + + if (address >= 12) // Global registers + { + switch (address) + { + case 12: // A/D (A to D Convert/Test) + if (bitfield(m_adc, 0)) // Writam_ble ADC + { + m_adc = (m_adc & 7) | (data & ~7); + m_intf.adc_w(m_adc & ~7); + } + m_adc = (m_adc & ~3) | (data & 3); + break; + case 13: // ACT (Number of voices) + m_active = std::min(24, bitfield(data, 0, 5)); + break; + case 14: // IRQV (Interrupting voice vector) + // Read only + break; + case 15: // PAGE (Page select register) + m_page = bitfield(data, 0, 6); + break; + } + } + else // Voice specific registers + { + const u8 voice = bitfield(page, 0, 5); // Voice select + if (voice < 25) + { + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 56 + { + switch (address) + { + case 1: // O4(n-1) (Filter 4 Temp Register) + v.filter().set_o4_1(sign_ext(data, 16)); + break; + case 2: // O3(n-2) (Filter 3 Temp Register #2) + v.filter().set_o3_2(sign_ext(data, 16)); + break; + case 3: // O3(n-1) (Filter 3 Temp Register #1) + v.filter().set_o3_1(sign_ext(data, 16)); + break; + case 4: // O2(n-2) (Filter 2 Temp Register #2) + v.filter().set_o2_2(sign_ext(data, 16)); + break; + case 5: // O2(n-1) (Filter 2 Temp Register #1) + v.filter().set_o2_1(sign_ext(data, 16)); + break; + case 6: // O1(n-1) (Filter 1 Temp Register) + v.filter().set_o1_1(sign_ext(data, 16)); + break; + } + } + else // Page 0 - 24 + { + switch (address) + { + case 0: // CR (Control Register) + v.alu().set_stop(bitfield(data, 0, 2)); + v.cr().set_adc(bitfield(data, 2)); + v.alu().set_lpe(bitfield(data, 3)); + v.alu().set_ble(bitfield(data, 4)); + v.alu().set_irqe(bitfield(data, 5)); + v.alu().set_dir(bitfield(data, 6)); + v.alu().set_irq(bitfield(data, 7)); + break; + case 1: // FC (Frequency Control) + v.alu().set_fc(bitfield(data, 1, 15)); + break; + case 2: // STRT-H (Loop Start Register High) + v.alu().set_start(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 3: // STRT-L (Loop Start Register Low) + v.alu().set_start(data & 0xffe0, 0xffe0); + break; + case 4: // END-H (Loop End Register High) + v.alu().set_end(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 5: // END-L (Loop End Register Low) + v.alu().set_end(data & 0xffe0, 0xffe0); + break; + case 6: // K2 (Filter Cutoff Coefficient #2) + v.filter().set_k2(data & 0xfff0); + break; + case 7: // K1 (Filter Cutoff Coefficient #1) + v.filter().set_k1(data & 0xfff0); + break; + case 8: // Volume + v.set_volume(bitfield(data, 4, 12)); + break; + case 9: // CA (Filter Config, Channel Assign) + v.cr().set_ca(bitfield(data, 0, 4)); + v.filter().set_lp(bitfield(data, 4, 2)); + break; + case 10: // ACCH (Accumulator High) + v.alu().set_accum(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 11: // ACCL (Accumulator Low) + v.alu().set_accum(data, 0xffff); + break; + } + } + } + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp new file mode 100644 index 000000000..d37ef36da --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp @@ -0,0 +1,117 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_ES5504_HPP +#define _VGSOUND_EMU_SRC_ES5504_HPP + +#pragma once + +#include "es550x.hpp" + +// ES5504 specific +class es5504_core : public es550x_shared_core +{ + private: + // es5504 voice classes + class voice_t : public es550x_voice_t + { + public: + // constructor + voice_t(es5504_core &host) + : es550x_voice_t("es5504_voice", 20, 9, false) + , m_host(host) + , m_volume(0) + , m_out(0) + { + } + + // internal state + virtual void reset() override; + virtual void fetch(u8 voice, u8 cycle) override; + virtual void tick(u8 voice) override; + + // setters + inline void set_volume(u16 volume) { m_volume = volume; } + + // getters + inline u16 volume() { return m_volume; } + + inline s32 out() { return m_out; } + + private: + void adc_exec(); + + // registers + es5504_core &m_host; + u16 m_volume = 0; // 12 bit Volume + s32 m_out = 0; // channel outputs + }; + + public: + // constructor + es5504_core(es550x_intf &intf) + : es550x_shared_core("es5504", 25, intf) + , m_voice{*this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this} + , m_adc(0) + , m_out{0} + { + } + + // host interface + u16 host_r(u8 address); + void host_w(u8 address, u16 data); + + // internal state + virtual void reset() override; + virtual void tick() override; + + // less cycle accurate, but also less cpu heavy update routine + void tick_perf(); + + // 16 analog output channels + inline s32 out(u8 ch) { return m_out[ch & 0xf]; } + + //----------------------------------------------------------------- + // + // for preview/debug purpose only, not for serious emulators + // + //----------------------------------------------------------------- + + // bypass chips host interface for debug purpose only + u16 read(u8 address, bool cpu_access = false); + void write(u8 address, u16 data, bool cpu_access = false); + + u16 regs_r(u8 page, u8 address, bool cpu_access = false); + void regs_w(u8 page, u8 address, u16 data, bool cpu_access = false); + + u16 regs_r(u8 page, u8 address) + { + u8 prev = m_page; + m_page = page; + u16 ret = read(address, false); + m_page = prev; + return ret; + } + + // per-voice outputs + inline s32 voice_out(u8 voice) { return (voice < 25) ? m_voice[voice].out() : 0; } + + protected: + virtual inline u8 max_voices() override { return 25; } + + virtual void voice_tick() override; + + private: + std::array m_voice; // 25 voices + u16 m_adc = 0; // ADC register + std::array m_out = {0}; // 16 channel outputs +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp new file mode 100644 index 000000000..07a7b7046 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp @@ -0,0 +1,652 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5505 emulation core +*/ + +#include "es5505.hpp" + +// Internal functions +void es5505_core::tick() +{ + m_voice_update = false; + m_voice_end = false; + // CLKIN + if (m_clkin.tick()) + { + // SERBCLK + if (m_clkin.edge().changed()) // BCLK is freely running clock + { + if (m_bclk.tick()) + { + m_intf.bclk(m_bclk.current_edge()); + // Serial output + if (m_bclk.falling_edge()) + { + // SERLRCLK + if (m_lrclk.tick()) + { + m_intf.lrclk(m_lrclk.current_edge()); + } + } + // SERWCLK + if (m_lrclk.edge().changed()) + { + m_wclk = 0; + } + if (m_bclk.falling_edge()) + { + if (m_wclk == ((m_sermode.sony_bb()) ? 1 : 0)) + { + if (m_lrclk.current_edge()) + { + for (int i = 0; i < 4; i++) + { + // copy output + m_output[i] = m_output_temp[i]; + m_output_latch[i] = m_ch[i]; + m_output_temp[i].reset(); + // clamp to 16 bit (upper 5 bits are overflow + // guard bits) + m_output_latch[i].clamp16(); + // set signed + if (m_output_latch[i].left() < 0) + { + m_output_temp[i].set_left(-1); + } + if (m_output_latch[i].right() < 0) + { + m_output_temp[i].set_right(-1); + } + } + } + m_wclk_lr = m_lrclk.current_edge(); + m_output_bit = 16; + } + s8 output_bit = --m_output_bit; + if (m_output_bit >= 0) + { + for (int i = 0; i < 4; i++) + { + if (m_wclk_lr) + { // Right output + m_output_temp[i].serial_in( + m_wclk_lr, + bitfield(m_output_latch[i].right(), output_bit)); + } + else + { // Left output + m_output_temp[i].serial_in( + m_wclk_lr, + bitfield(m_output_latch[i].left(), output_bit)); + } + } + } + m_wclk++; + } + } + } + // /CAS, E + if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock + { + // /CAS + if (m_cas.tick()) + { + // /CAS high, E low: get sample address + if (m_cas.falling_edge()) + { + // /CAS low, E low: fetch sample + if (!m_e.current_edge()) + { + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + } + // E + if (m_e.tick()) + { + m_intf.e_pin(m_e.current_edge()); + if (m_e.rising_edge()) // Host access + { + m_host_intf.update_strobe(); + voice_tick(); + } + else if (m_e.falling_edge()) // Voice memory + { + m_host_intf.clear_host_access(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + if (m_e.current_edge()) // Host interface + { + if (m_host_intf.host_access()) + { + if (m_host_intf.rw() && (m_e.cycle() == 2)) // Read + { + m_hd = read(m_ha); + m_host_intf.clear_host_access(); + } + else if ((!m_host_intf.rw()) && (m_e.cycle() == 2)) + { // Write + write(m_ha, m_hd); + } + } + } + else if (!m_e.current_edge()) + { + if (m_e.cycle() == 2) + { + // reset host access state + m_hd = 0; + m_host_intf.clear_strobe(); + } + } + } + } + } +} + +// less cycle accurate, but less CPU heavy routine +void es5505_core::tick_perf() +{ + m_voice_update = false; + m_voice_end = false; + // output + for (int c = 0; c < 4; c++) + { + m_output[c] = m_ch[c]; + } + + // update + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); +} + +void es5505_core::voice_tick() +{ + // Voice updates every 2 E clock cycle (or 4 BCLK clock cycle) + m_voice_update = bitfield(m_voice_fetch++, 0); + if (m_voice_update) + { + // Update voice + m_voice[m_voice_cycle].tick(m_voice_cycle); + + // Refresh output + if ((++m_voice_cycle) > clamp(m_active, 7, 31)) // 8 ~ 32 voices + { + m_voice_end = true; + m_voice_cycle = 0; + for (auto &elem : m_ch) + { + elem.reset(); + } + + for (auto &elem : m_voice) + { + m_ch[bitfield(elem.cr().ca(), 0, 2)] += elem.ch(); + elem.ch().reset(); + } + } + m_voice_fetch = 0; + } +} + +void es5505_core::voice_t::fetch(u8 voice, u8 cycle) +{ + m_alu.set_sample( + cycle, + m_host.m_intf.read_sample(voice, + bitfield(m_cr.bs(), 0), + bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer))); +} + +void es5505_core::voice_t::tick(u8 voice) +{ + m_ch.reset(); + + // Filter execute + m_filter.tick(m_alu.interpolation()); + + if (m_alu.busy()) + { + // Send to output + m_ch.set_left(volume_calc(m_lvol, sign_ext(m_filter.o4_1(), 16))); + m_ch.set_right(volume_calc(m_rvol, sign_ext(m_filter.o4_1(), 16))); + + // ALU execute + if (m_alu.tick()) + { + m_alu.loop_exec(); + } + } + + // Update IRQ + m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice); +} + +// volume calculation +s32 es5505_core::voice_t::volume_calc(u8 volume, s32 in) +{ + u8 exponent = bitfield(volume, 4, 4); + u8 mantissa = bitfield(volume, 0, 4); + return exponent ? (in * s32(0x10 | mantissa)) >> (19 - exponent) : 0; +} + +void es5505_core::reset() +{ + es550x_shared_core::reset(); + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_sermode.reset(); + m_bclk.reset(); + m_lrclk.reset(); + m_wclk = 0; + m_wclk_lr = false; + m_output_bit = 0; + for (auto &elem : m_ch) + { + elem.reset(); + } + for (auto &elem : m_output) + { + elem.reset(); + } + for (auto &elem : m_output_temp) + { + elem.reset(); + } + for (auto &elem : m_output_latch) + { + elem.reset(); + } +} + +void es5505_core::voice_t::reset() +{ + es550x_shared_core::es550x_voice_t::reset(); + m_lvol = 0; + m_rvol = 0; + m_ch.reset(); +} + +// Accessors +u16 es5505_core::host_r(u8 address) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + if (m_e.rising_edge()) + { // update directly + m_hd = read(m_ha, true); + } + else + { + m_host_intf.set_strobe(true); + } + } + return m_hd; +} + +void es5505_core::host_w(u8 address, u16 data) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + m_hd = data; + if (m_e.rising_edge()) + { // update directly + write(m_ha, m_hd, true); + } + else + { + m_host_intf.set_strobe(false); + } + } +} + +u16 es5505_core::read(u8 address, bool cpu_access) { return regs_r(m_page, address, cpu_access); } + +void es5505_core::write(u8 address, u16 data, bool cpu_access) +{ + regs_w(m_page, address, data, cpu_access); +} + +u16 es5505_core::regs_r(u8 page, u8 address, bool cpu_access) +{ + u16 ret = 0xffff; + address = bitfield(address, 0, 4); // 4 bit address for CPU access + + if (address >= 13) // Global registers + { + switch (address) + { + case 13: // ACT (Number of voices) + ret = (ret & ~0x1f) | bitfield(m_active, 0, 5); + break; + case 14: // IRQV (Interrupting voice vector) + ret = (ret & ~0x9f) | m_irqv.get(); + if (cpu_access) + { + m_irqv.clear(); + if (bool(bitfield(ret, 7)) != m_irqv.irqb()) + { + m_voice[m_irqv.voice()].alu().irq_update(m_intf, m_irqv); + } + } + break; + case 15: // PAGE (Page select register) + ret = (ret & ~0x7f) | bitfield(m_page, 0, 7); + break; + } + } + else + { + if (bitfield(page, 6)) // Channel registers + { + switch (address) + { + case 0: // CH0L (Channel 0 Left) + case 2: // CH1L (Channel 1 Left) + case 4: // CH2L (Channel 2 Left) + if (!cpu_access) + { // CPU can't read here + ret = m_ch[bitfield(address, 0, 2)].left(); + } + break; + case 1: // CH0R (Channel 0 Right) + case 3: // CH1R (Channel 1 Right) + case 5: // CH2R (Channel 2 Right) + if (!cpu_access) + { // CPU can't read here + ret = m_ch[bitfield(address, 0, 2)].right(); + } + break; + case 6: // CH3L (Channel 3 Left) + if ((!cpu_access) || m_sermode.adc()) + { + ret = m_ch[3].left(); + } + break; + case 7: // CH3R (Channel 3 Right) + if ((!cpu_access) || m_sermode.adc()) + { + ret = m_ch[3].right(); + } + break; + case 8: // SERMODE (Serial Mode) + ret = (ret & ~0xf807) | (m_sermode.adc() ? 0x01 : 0x00) | + (m_sermode.test() ? 0x02 : 0x00) | (m_sermode.sony_bb() ? 0x04 : 0x00) | + (bitfield(m_sermode.msb(), 0, 5) << 11); + break; + case 9: // PAR (Port A/D Register) + ret = (ret & ~0x3f) | (m_intf.adc_r() & ~0x3f); + break; + } + } + else // Voice specific registers + { + const u8 voice = bitfield(page, 0, 5); // Voice select + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 63 + { + switch (address) + { + case 1: // O4(n-1) (Filter 4 Temp Register) + ret = v.filter().o4_1(); + break; + case 2: // O3(n-2) (Filter 3 Temp Register #2) + ret = v.filter().o3_2(); + break; + case 3: // O3(n-1) (Filter 3 Temp Register #1) + ret = v.filter().o3_1(); + break; + case 4: // O2(n-2) (Filter 2 Temp Register #2) + ret = v.filter().o2_2(); + break; + case 5: // O2(n-1) (Filter 2 Temp Register #1) + ret = v.filter().o2_1(); + break; + case 6: // O1(n-1) (Filter 1 Temp Register) + ret = v.filter().o1_1(); + break; + } + } + else // Page 0 - 31 + { + switch (address) + { + case 0: // CR (Control Register) + ret = (ret & ~0xfff) | (v.alu().stop() << 0) | + (bitfield(v.cr().bs(), 0) ? 0x04 : 0x00) | + (v.alu().lpe() ? 0x08 : 0x00) | (v.alu().ble() ? 0x10 : 0x00) | + (v.alu().irqe() ? 0x20 : 0x00) | (v.alu().dir() ? 0x40 : 0x00) | + (v.alu().irq() ? 0x80 : 0x00) | (bitfield(v.cr().ca(), 0, 2) << 8) | + (bitfield(v.filter().lp(), 0, 2) << 10); + break; + case 1: // FC (Frequency Control) + ret = (ret & ~0xfffe) | (bitfield(v.alu().fc(), 0, 15) << 1); + break; + case 2: // STRT-H (Loop Start Register High) + ret = (ret & ~0x1fff) | bitfield(v.alu().start(), 16, 13); + break; + case 3: // STRT-L (Loop Start Register Low) + ret = (ret & ~0xffe0) | (v.alu().start() & 0xffe0); + break; + case 4: // END-H (Loop End Register High) + ret = (ret & ~0x1fff) | bitfield(v.alu().end(), 16, 13); + break; + case 5: // END-L (Loop End Register Low) + ret = (ret & ~0xffe0) | (v.alu().end() & 0xffe0); + break; + case 6: // K2 (Filter Cutoff Coefficient #2) + ret = (ret & ~0xfff0) | (v.filter().k2() & 0xfff0); + break; + case 7: // K1 (Filter Cutoff Coefficient #1) + ret = (ret & ~0xfff0) | (v.filter().k1() & 0xfff0); + break; + case 8: // LVOL (Left Volume) + ret = (ret & ~0xff00) | ((v.lvol() << 8) & 0xff00); + break; + case 9: // RVOL (Right Volume) + ret = (ret & ~0xff00) | ((v.rvol() << 8) & 0xff00); + break; + case 10: // ACCH (Accumulator High) + ret = (ret & ~0x1fff) | bitfield(v.alu().accum(), 16, 13); + break; + case 11: // ACCL (Accumulator Low) + ret = bitfield(v.alu().accum(), 0, 16); + break; + } + } + } + } + + return ret; +} + +void es5505_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access) +{ + address = bitfield(address, 0, 4); // 4 bit address for CPU access + + if (address >= 12) // Global registers + { + switch (address) + { + case 13: // ACT (Number of voices) + m_active = std::max(7, bitfield(data, 0, 5)); + break; + case 14: // IRQV (Interrupting voice vector) + // Read only + break; + case 15: // PAGE (Page select register) + m_page = bitfield(data, 0, 7); + break; + } + } + else // Voice specific registers + { + if (bitfield(page, 6)) // Channel registers + { + switch (address) + { + case 0: // CH0L (Channel 0 Left) + if (m_sermode.test()) + { + m_ch[0].set_left(data); + } + break; + case 1: // CH0R (Channel 0 Right) + if (m_sermode.test()) + { + m_ch[0].set_right(data); + } + break; + case 2: // CH1L (Channel 1 Left) + if (m_sermode.test()) + { + m_ch[1].set_left(data); + } + break; + case 3: // CH1R (Channel 1 Right) + if (m_sermode.test()) + { + m_ch[1].set_right(data); + } + break; + case 4: // CH2L (Channel 2 Left) + if (m_sermode.test()) + { + m_ch[2].set_left(data); + } + break; + case 5: // CH2R (Channel 2 Right) + if (m_sermode.test()) + { + m_ch[2].set_right(data); + } + break; + case 6: // CH3L (Channel 3 Left) + if (m_sermode.test()) + { + m_ch[3].set_left(data); + } + break; + case 7: // CH3R (Channel 3 Right) + if (m_sermode.test()) + { + m_ch[3].set_right(data); + } + break; + case 8: // SERMODE (Serial Mode) + m_sermode.write(data); + break; + case 9: // PAR (Port A/D Register) + // Read only + break; + } + } + else // Voice specific registers + { + const u8 voice = bitfield(page, 0, 5); // Voice select + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 56 + { + switch (address) + { + case 1: // O4(n-1) (Filter 4 Temp Register) + v.filter().set_o4_1(sign_ext(data, 16)); + break; + case 2: // O3(n-2) (Filter 3 Temp Register #2) + v.filter().set_o3_2(sign_ext(data, 16)); + break; + case 3: // O3(n-1) (Filter 3 Temp Register #1) + v.filter().set_o3_1(sign_ext(data, 16)); + break; + case 4: // O2(n-2) (Filter 2 Temp Register #2) + v.filter().set_o2_2(sign_ext(data, 16)); + break; + case 5: // O2(n-1) (Filter 2 Temp Register #1) + v.filter().set_o2_1(sign_ext(data, 16)); + break; + case 6: // O1(n-1) (Filter 1 Temp Register) + v.filter().set_o1_1(sign_ext(data, 16)); + break; + } + } + else // Page 0 - 24 + { + switch (address) + { + case 0: // CR (Control Register) + v.alu().set_stop(bitfield(data, 0, 2)); + v.cr().set_bs(bitfield(data, 2)); + v.alu().set_lpe(bitfield(data, 3)); + v.alu().set_ble(bitfield(data, 4)); + v.alu().set_irqe(bitfield(data, 5)); + v.alu().set_dir(bitfield(data, 6)); + v.alu().set_irq(bitfield(data, 7)); + v.cr().set_ca(bitfield(data, 8, 2)); + v.filter().set_lp(bitfield(data, 10, 2)); + break; + case 1: // FC (Frequency Control) + v.alu().set_fc(bitfield(data, 1, 15)); + break; + case 2: // STRT-H (Loop Start Register High) + v.alu().set_start(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 3: // STRT-L (Loop Start Register Low) + v.alu().set_start(data & 0xffe0, 0xffe0); + break; + case 4: // END-H (Loop End Register High) + v.alu().set_end(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 5: // END-L (Loop End Register Low) + v.alu().set_end(data & 0xffe0, 0xffe0); + break; + case 6: // K2 (Filter Cutoff Coefficient #2) + v.filter().set_k2(data & 0xfff0); + break; + case 7: // K1 (Filter Cutoff Coefficient #1) + v.filter().set_k1(data & 0xfff0); + break; + case 8: // LVOL (Left Volume) + v.set_lvol(bitfield(data, 8, 8)); + break; + case 9: // RVOL (Right Volume) + v.set_rvol(bitfield(data, 8, 8)); + break; + case 10: // ACCH (Accumulator High) + v.alu().set_accum(bitfield(data, 0, 13) << 16, 0x1fff0000); + break; + case 11: // ACCL (Accumulator Low) + v.alu().set_accum(data, 0xffff); + break; + } + } + } + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp new file mode 100644 index 000000000..34782f104 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -0,0 +1,304 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_ES5505_HPP +#define _VGSOUND_EMU_SRC_ES5505_HPP + +#pragma once + +#include "es550x.hpp" + +// ES5505 specific +class es5505_core : public es550x_shared_core +{ + private: + class output_t : public vgsound_emu_core + { + public: + output_t(s32 left = 0, s32 right = 0) + : vgsound_emu_core("es5505_output") + , m_left(left) + , m_right(right) + { + } + + void reset() + { + m_left = 0; + m_right = 0; + }; + + inline s32 clamp16(s32 in) { return clamp(in, -0x8000, 0x7fff); } + + inline void clamp16(output_t src) + { + m_left = clamp16(src.left()); + m_right = clamp16(src.right()); + } + + inline void clamp16() + { + m_left = clamp16(m_left); + m_right = clamp16(m_right); + } + + // setters + inline void set_left(s32 left) { m_left = left; } + + inline void set_right(s32 right) { m_right = right; } + + inline void serial_in(bool ch, u8 in) + { + if (ch) // Right output + { + m_right = (m_right << 1) | (in ? 1 : 0); + } + else // Left output + { + m_left = (m_left << 1) | (in ? 1 : 0); + } + } + + // getters + inline u32 left() { return m_left; } + + inline u32 right() { return m_right; } + + output_t &operator+=(output_t &src) + { + m_left = clamp16(m_left + src.left()); + m_right = clamp16(m_right + src.right()); + return *this; + } + + output_t &operator=(output_t src) + { + clamp16(src); + return *this; + } + + output_t &operator=(s32 val) + { + m_left = m_right = clamp16(val); + return *this; + } + + output_t &operator>>(s32 shift) + { + m_left >>= shift; + m_right >>= shift; + return *this; + } + + private: + s32 m_left = 0; + s32 m_right = 0; + }; + + // es5505 voice classes + class voice_t : public es550x_voice_t + { + public: + // constructor + voice_t(es5505_core &host) + : es550x_voice_t("es5505_voice", 20, 9, false) + , m_host(host) + , m_lvol(0) + , m_rvol(0) + , m_ch(output_t()) + { + } + + // internal state + virtual void reset() override; + virtual void fetch(u8 voice, u8 cycle) override; + virtual void tick(u8 voice) override; + + // setters + inline void set_lvol(u8 lvol) { m_lvol = lvol; } + + inline void set_rvol(u8 rvol) { m_rvol = rvol; } + + // getters + inline u8 lvol() { return m_lvol; } + + inline u8 rvol() { return m_rvol; } + + output_t &ch() { return m_ch; } + + private: + s32 volume_calc(u8 volume, s32 in); + + // registers + es5505_core &m_host; + u8 m_lvol = 0; // Left volume + u8 m_rvol = 0; // Right volume + output_t m_ch; // channel output + }; + + class sermode_t : public vgsound_emu_core + { + public: + sermode_t() + : vgsound_emu_core("es5505_sermode") + , m_adc(0) + , m_test(0) + , m_sony_bb(0) + , m_msb(0) + { + } + + void reset() + { + m_adc = 0; + m_test = 0; + m_sony_bb = 0; + m_msb = 0; + } + + // setters + void write(u16 data) + { + m_adc = (data >> 0) & 1; + m_test = (data >> 1) & 1; + m_sony_bb = (data >> 2) & 1; + m_msb = (data >> 11) & 0x1f; + } + + void set_adc(bool adc) { m_adc = adc ? 1 : 0; } + + void set_test(bool test) { m_test = test ? 1 : 0; } + + void set_sony_bb(bool sony_bb) { m_sony_bb = sony_bb ? 1 : 0; } + + void set_msb(u8 msb) { m_msb = msb & 0x1f; } + + // getters + bool adc() { return m_adc; } + + bool test() { return m_test; } + + bool sony_bb() { return m_sony_bb; } + + u8 msb() { return m_msb; } + + private: + u8 m_adc : 1; // A/D + u8 m_test : 1; // Test + u8 m_sony_bb : 1; // Sony/BB format serial output + u8 m_msb : 5; // Serial output MSB + }; + + public: + // constructor + es5505_core(es550x_intf &intf) + : es550x_shared_core("es5505", 32, intf) + , m_voice{*this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, *this} + , m_sermode(sermode_t()) + , m_bclk(clock_pulse_t(4, 0)) + , m_lrclk(clock_pulse_t(16, 1)) + , m_wclk(0) + , m_wclk_lr(false) + , m_output_bit(0) + , m_ch{output_t()} + , m_output{output_t()} + , m_output_temp{output_t()} + , m_output_latch{output_t()} + { + } + + // host interface + u16 host_r(u8 address); + void host_w(u8 address, u16 data); + + // internal state + virtual void reset() override; + virtual void tick() override; + + // less cycle accurate, but also less cpu heavy update routine + void tick_perf(); + + // clock outputs + inline bool bclk() { return m_bclk.current_edge(); } + + inline bool bclk_rising_edge() { return m_bclk.rising_edge(); } + + inline bool bclk_falling_edge() { return m_bclk.falling_edge(); } + + // Input mode for Channel 3 + inline void lin(s32 in) + { + if (m_sermode.adc()) + { + m_ch[3].set_left(in); + } + } + + inline void rin(s32 in) + { + if (m_sermode.adc()) + { + m_ch[3].set_right(in); + } + } + + // 4 stereo output channels + inline s32 lout(u8 ch) { return m_ch[ch & 0x3].left(); } + + inline s32 rout(u8 ch) { return m_ch[ch & 0x3].right(); } + + //----------------------------------------------------------------- + // + // for preview/debug purpose only, not for serious emulators + // + //----------------------------------------------------------------- + + // bypass chips host interface for debug purpose only + u16 read(u8 address, bool cpu_access = false); + void write(u8 address, u16 data, bool cpu_access = false); + + u16 regs_r(u8 page, u8 address, bool cpu_access = false); + void regs_w(u8 page, u8 address, u16 data, bool cpu_access = false); + + u16 regs_r(u8 page, u8 address) + { + u8 prev = m_page; + m_page = page; + u16 ret = read(address, false); + m_page = prev; + return ret; + } + + // per-voice outputs + inline s32 voice_lout(u8 voice) { return (voice < 32) ? m_voice[voice].ch().left() : 0; } + + inline s32 voice_rout(u8 voice) { return (voice < 32) ? m_voice[voice].ch().right() : 0; } + + protected: + virtual inline u8 max_voices() override { return 32; } + + virtual void voice_tick() override; + + private: + std::array m_voice; // 32 voices + // Serial related stuffs + sermode_t m_sermode; // Serial mode register + clock_pulse_t m_bclk; // BCLK clock (CLKIN / 4), freely running clock + clock_pulse_t m_lrclk; // LRCLK + s16 m_wclk = 0; // WCLK + bool m_wclk_lr = false; // WCLK, L/R output select + s8 m_output_bit = 0; // Bit position in output + std::array m_ch; // 4 stereo output channels + std::array m_output; // Serial outputs + std::array m_output_temp; // temporary signal for serial output + std::array m_output_latch; // output latch +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp new file mode 100644 index 000000000..ebb9a62d9 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp @@ -0,0 +1,870 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5506 emulation core +*/ + +#include "es5506.hpp" + +// Internal functions +void es5506_core::tick() +{ + m_voice_update = false; + m_voice_end = false; + // CLKIN + if (m_clkin.tick()) + { + // BCLK + if (m_clkin.edge().changed() && (!m_mode.bclk_en())) // BCLK is freely running clock + { + if (m_bclk.tick()) + { + m_intf.bclk(m_bclk.current_edge()); + // Serial output + if (!m_mode.lrclk_en()) + { + if (m_bclk.falling_edge()) + { + // LRCLK + if (m_lrclk.tick()) + { + m_intf.lrclk(m_lrclk.current_edge()); + if (m_lrclk.rising_edge()) + { + m_w_st_curr = m_w_st; + m_w_end_curr = m_w_end; + } + if (m_lrclk.falling_edge()) + { // update width + m_lrclk.set_width_latch(m_lr_end); + } + } + } + } + // WCLK + if (!m_mode.wclk_en()) + { + if (!m_mode.lrclk_en()) + { + if (m_lrclk.edge().changed()) + { + m_wclk = 0; + } + } + if (m_bclk.falling_edge()) + { + if (m_wclk == m_w_st_curr) + { + m_intf.wclk(true); + if (m_lrclk.current_edge()) + { + for (int i = 0; i < 6; i++) + { + // copy output + m_output[i] = m_output_temp[i]; + m_output_latch[i] = m_ch[i]; + m_output_temp[i].reset(); + // clamp to 20 bit (upper 3 bits are + // overflow guard bits) + m_output_latch[i].clamp20(); + // set signed + if (m_output_latch[i].left() < 0) + { + m_output_temp[i].set_left(-1); + } + if (m_output_latch[i].right() < 0) + { + m_output_temp[i].set_right(-1); + } + } + } + m_wclk_lr = m_lrclk.current_edge(); + m_output_bit = 20; + } + if (m_wclk < m_w_end_curr) + { + s8 output_bit = --m_output_bit; + if (m_output_bit >= 0) + { + for (int i = 0; i < 6; i++) + { + if (m_wclk_lr) + { + // Right output + m_output_temp[i].serial_in( + m_wclk_lr, + bitfield(m_output_latch[i].right(), output_bit)); + } + else + { + // Left output + m_output_temp[i].serial_in( + m_wclk_lr, + bitfield(m_output_latch[i].left(), output_bit)); + } + } + } + } + if (m_wclk == m_w_end_curr) + { + m_intf.wclk(false); + } + + m_wclk++; + } + } + } + } + // /CAS, E + if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock + { + // /CAS + if (m_cas.tick()) + { + // single OTTO master mode, /CAS high, E low: get sample address + // single OTTO early mode, /CAS falling, E high: get sample + // address + if (m_cas.falling_edge()) + { + if (!m_e.current_edge()) + { + // single OTTO master mode, /CAS low, E low: fetch + // sample + if (m_mode.master()) + { + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + else if (m_e.current_edge()) + { + // dual OTTO slave mode, /CAS low, E high: fetch sample + if (m_mode.dual() && (!m_mode.master())) + { // Dual OTTO, slave mode + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + } + } + } + } + // E + if (m_e.tick()) + { + m_intf.e_pin(m_e.current_edge()); + if (m_e.rising_edge()) + { + m_host_intf.update_strobe(); + } + else if (m_e.falling_edge()) + { + m_host_intf.clear_host_access(); + voice_tick(); + } + if (m_e.current_edge()) // Host interface + { + if (m_host_intf.host_access()) + { + if (m_host_intf.rw() && (m_e.cycle() == 0)) // Read + { + m_hd = read(m_ha); + m_host_intf.clear_host_access(); + } + else if ((!m_host_intf.rw()) && (m_e.cycle() == 2)) + { // Write + write(m_ha, m_hd); + } + } + } + else if (!m_e.current_edge()) + { + if (m_e.cycle() == 2) + { + // reset host access state + m_hd = 0; + m_host_intf.clear_strobe(); + } + } + } + } + } +} + +// less cycle accurate, but less CPU heavy routine +void es5506_core::tick_perf() +{ + m_voice_update = false; + m_voice_end = false; + // output + if (((!m_mode.lrclk_en()) && (!m_mode.bclk_en()) && (!m_mode.wclk_en())) && (m_w_st < m_w_end)) + { + const int output_bits = 20 - (m_w_end - m_w_st); + if (output_bits < 20) + { + for (int c = 0; c < 6; c++) + { + m_output[c] = m_ch[c] >> output_bits; + } + } + } + else + { + for (int c = 0; c < 6; c++) + { + m_output[c] = 0; + } + } + + // update + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); + // falling edge + m_e.edge().set(false); + m_intf.e_pin(false); + m_host_intf.clear_host_access(); + m_host_intf.clear_strobe(); + m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); + voice_tick(); + // rising edge + m_e.edge().set(true); + m_intf.e_pin(true); + m_host_intf.update_strobe(); +} + +void es5506_core::voice_tick() +{ + // Voice updates every 2 E clock cycle (or 4 BCLK clock cycle) + m_voice_update = bitfield(m_voice_fetch++, 0); + if (m_voice_update) + { + // Update voice + m_voice[m_voice_cycle].tick(m_voice_cycle); + + // Refresh output + if ((++m_voice_cycle) > clamp(m_active, 4, 31)) // 5 ~ 32 voices + { + m_voice_end = true; + m_voice_cycle = 0; + for (output_t &elem : m_ch) + { + elem.reset(); + } + + for (voice_t &elem : m_voice) + { + const u8 ca = bitfield(elem.cr().ca(), 0, 3); + if (ca < 6) + { + m_ch[ca] += elem.ch(); + } + elem.ch().reset(); + } + } + m_voice_fetch = 0; + } +} + +void es5506_core::voice_t::fetch(u8 voice, u8 cycle) +{ + m_alu.set_sample( + cycle, + m_host.m_intf.read_sample(voice, + m_cr.bs(), + bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer))); + if (m_cr.cmpd()) + { // Decompress (Upper 8 bit is used for compressed format) + m_alu.set_sample(cycle, decompress(bitfield(m_alu.sample(cycle), 8, 8))); + } +} + +void es5506_core::voice_t::tick(u8 voice) +{ + m_ch.reset(); + + // Filter execute + m_filter.tick(m_alu.interpolation()); + + if (m_alu.busy()) + { + // Send to output + m_ch.set_left(volume_calc(m_lvol, sign_ext(m_filter.o4_1(), 16))); + m_ch.set_right(volume_calc(m_rvol, sign_ext(m_filter.o4_1(), 16))); + + // ALU execute + if (m_alu.tick()) + { + m_alu.loop_exec(); + } + } + // Envelope + if (m_ecount != 0) + { + // Left and Right volume + if (bitfield(m_lvramp, 0, 8) != 0) + { + m_lvol = clamp(m_lvol + sign_ext(bitfield(m_lvramp, 0, 8), 8), 0, 0xffff); + } + if (bitfield(m_rvramp, 0, 8) != 0) + { + m_rvol = clamp(m_rvol + sign_ext(bitfield(m_rvramp, 0, 8), 8), 0, 0xffff); + } + + // Filter coeffcient + if ((m_k1ramp.ramp() != 0) && + ((m_k1ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0))) + { + m_filter.set_k1( + clamp(m_filter.k1() + sign_ext(m_k1ramp.ramp(), 8), 0, 0xffff)); + } + if ((m_k2ramp.ramp() != 0) && + ((m_k2ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0))) + { + m_filter.set_k2( + clamp(m_filter.k2() + sign_ext(m_k2ramp.ramp(), 8), 0, 0xffff)); + } + + m_ecount--; + } + m_filtcount = bitfield(m_filtcount + 1, 0, 3); + + // Update IRQ + m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice); +} + +// Compressed format +s16 es5506_core::voice_t::decompress(u8 sample) +{ + u8 exponent = bitfield(sample, 5, 3); + u8 mantissa = bitfield(sample, 0, 5); + return (exponent > 0) + ? s16(((bitfield(mantissa, 4) ? 0x10 : ~0x1f) | bitfield(mantissa, 0, 4)) + << (4 + (exponent - 1))) + : s16(((bitfield(mantissa, 4) ? ~0xf : 0) | bitfield(mantissa, 0, 4)) << 4); +} + +// volume calculation +s32 es5506_core::voice_t::volume_calc(u16 volume, s32 in) +{ + u8 exponent = bitfield(volume, 12, 4); + u8 mantissa = bitfield(volume, 4, 8); + return (in * s32(0x100 | mantissa)) >> (20 - exponent); +} + +void es5506_core::reset() +{ + es550x_shared_core::reset(); + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_read_latch = 0xffffffff; + m_write_latch = 0xffffffff; + m_w_st = 0; + m_w_end = 0; + m_lr_end = 0; + m_w_st_curr = 0; + m_w_end_curr = 0; + m_mode.reset(); + m_bclk.reset(); + m_lrclk.reset(32); + m_wclk = 0; + m_wclk_lr = false; + m_output_bit = 0; + for (auto &elem : m_ch) + { + elem.reset(); + } + for (auto &elem : m_output) + { + elem.reset(); + } + for (auto &elem : m_output_temp) + { + elem.reset(); + } + for (auto &elem : m_output_latch) + { + elem.reset(); + } +} + +void es5506_core::voice_t::reset() +{ + es550x_shared_core::es550x_voice_t::reset(); + m_lvol = 0; + m_rvol = 0; + m_lvramp = 0; + m_rvramp = 0; + m_ecount = 0; + m_k2ramp.reset(); + m_k1ramp.reset(); + m_filtcount = 0; + m_ch.reset(); + m_mute = false; +} + +// Accessors +u8 es5506_core::host_r(u8 address) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + if (m_e.rising_edge()) + { // update directly + m_hd = read(m_ha, true); + } + else + { + m_host_intf.set_strobe(true); + } + } + return m_hd; +} + +void es5506_core::host_w(u8 address, u8 data) +{ + if (!m_host_intf.host_access()) + { + m_ha = address; + m_hd = data; + if (m_e.rising_edge()) + { // update directly + write(m_ha, m_hd, true); + } + else + { + m_host_intf.set_strobe(false); + } + } +} + +u8 es5506_core::read(u8 address, bool cpu_access) +{ + const u8 byte = bitfield(address, 0, 2); // byte select + const u8 shift = 24 - (byte << 3); + if (byte != 0) + { // Return already latched register if not highest byte is accessing + return bitfield(m_read_latch, shift, 8); + } + + address = bitfield(address, 2, 4); // 4 bit address for CPU access + + // get read register + m_read_latch = regs_r(m_page, address, cpu_access); + + return bitfield(m_read_latch, 24, 8); +} + +void es5506_core::write(u8 address, u8 data, bool cpu_access) +{ + const u8 byte = bitfield(address, 0, 2); // byte select + const u8 shift = 24 - (byte << 3); + address = bitfield(address, 2, 4); // 4 bit address for CPU access + + // Update register latch + m_write_latch = (m_write_latch & ~(0xff << shift)) | (u32(data) << shift); + + if (byte != 3) + { // Wait until lowest byte is writed + return; + } + + regs_w(m_page, address, m_write_latch, cpu_access); + + // Reset latch + m_write_latch = 0; +} + +u32 es5506_core::regs_r(u8 page, u8 address, bool cpu_access) +{ + u32 read_latch = 0xffffffff; + // Global registers + if (address >= 13) + { + switch (address) + { + case 13: // POT (Pot A/D Register) + read_latch = (read_latch & ~0x3ff) | bitfield(m_intf.adc_r(), 0, 10); + break; + case 14: // IRQV (Interrupting voice vector) + read_latch = (read_latch & ~0x9f) | (m_irqv.irqb() ? 0x80 : 0) | + bitfield(m_irqv.voice(), 0, 5); + if (cpu_access) + { + m_irqv.clear(); + if (bool(bitfield(read_latch, 7)) != m_irqv.irqb()) + { + m_voice[m_irqv.voice()].irq_update(m_intf, m_irqv); + } + } + break; + case 15: // PAGE (Page select register) + read_latch = (read_latch & ~0x7f) | bitfield(m_page, 0, 7); + break; + } + } + else + { + // Channel registers are Write only + if (bitfield(page, 6)) + { + if (!cpu_access) // CPU can't read here + { + switch (address) + { + case 0: // CH0L (Channel 0 Left) + case 2: // CH1L (Channel 1 Left) + case 4: // CH2L (Channel 2 Left) + case 6: // CH3L (Channel 3 Left) + case 8: // CH4L (Channel 4 Left) + case 10: // CH5L (Channel 5 Left) + read_latch = m_ch[bitfield(address, 1, 3)].left(); + break; + case 1: // CH0R (Channel 0 Right) + case 3: // CH1R (Channel 1 Right) + case 5: // CH2R (Channel 2 Right) + case 7: // CH3R (Channel 3 Right) + case 9: // CH4R (Channel 4 Right) + case 11: // CH5R (Channel 5 Right) + read_latch = m_ch[bitfield(address, 1, 3)].right(); + break; + } + } + } + else + { + const u8 voice = bitfield(page, 0, 5); // Voice select + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 63 + { + switch (address) + { + case 0: // CR (Control Register) + read_latch = (read_latch & ~0xffff) | (v.alu().stop() << 0) | + (v.alu().lei() ? 0x0004 : 0x0000) | (v.alu().loop() << 3) | + (v.alu().irqe() ? 0x0020 : 0x0000) | + (v.alu().dir() ? 0x0040 : 0x0000) | + (v.alu().irq() ? 0x0080 : 0x0000) | + (bitfield(v.filter().lp(), 0, 2) << 8) | (v.cr().ca() << 10) | + (v.cr().cmpd() ? 0x2000 : 0x0000) | (v.cr().bs() << 14); + break; + case 1: // START (Loop Start Register) + read_latch = (read_latch & ~0xfffff800) | (v.alu().start() & 0xfffff800); + break; + case 2: // END (Loop End Register) + read_latch = (read_latch & ~0xffffff80) | (v.alu().end() & 0xffffff80); + break; + case 3: // ACCUM (Accumulator Register) + read_latch = v.alu().accum(); + break; + case 4: // O4(n-1) (Filter 4 Temp Register) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o4_1(), 0, 18); + } + else + { + read_latch = v.filter().o4_1(); + } + break; + case 5: // O3(n-2) (Filter 3 Temp Register #2) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o3_2(), 0, 18); + } + else + { + read_latch = v.filter().o3_2(); + } + break; + case 6: // O3(n-1) (Filter 3 Temp Register #1) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o3_1(), 0, 18); + } + else + { + read_latch = v.filter().o3_1(); + } + break; + case 7: // O2(n-2) (Filter 2 Temp Register #2) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o2_2(), 0, 18); + } + else + { + read_latch = v.filter().o2_2(); + } + break; + case 8: // O2(n-1) (Filter 2 Temp Register #1) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o2_1(), 0, 18); + } + else + { + read_latch = v.filter().o2_1(); + } + break; + case 9: // O1(n-1) (Filter 1 Temp Register) + if (cpu_access) + { + read_latch = + (read_latch & ~0x3ffff) | bitfield(v.filter().o1_1(), 0, 18); + } + else + { + read_latch = v.filter().o1_1(); + } + break; + case 10: // W_ST (Word Clock Start Register) + read_latch = (read_latch & ~0x7f) | bitfield(m_w_st, 0, 7); + break; + case 11: // W_END (Word Clock End Register) + read_latch = (read_latch & ~0x7f) | bitfield(m_w_end, 0, 7); + break; + case 12: // LR_END (Left/Right Clock End Register) + read_latch = (read_latch & ~0x7f) | bitfield(m_lr_end, 0, 7); + break; + } + } + else // Page 0 - 31 + { + switch (address) + { + case 0: // CR (Control Register) + read_latch = (read_latch & ~0xffff) | (v.alu().stop() << 0) | + (v.alu().lei() ? 0x0004 : 0x0000) | (v.alu().loop() << 3) | + (v.alu().irqe() ? 0x0020 : 0x0000) | + (v.alu().dir() ? 0x0040 : 0x0000) | + (v.alu().irq() ? 0x0080 : 0x0000) | + (bitfield(v.filter().lp(), 0, 2) << 8) | (v.cr().ca() << 10) | + (v.cr().cmpd() ? 0x2000 : 0x0000) | (v.cr().bs() << 14); + break; + case 1: // FC (Frequency Control) + read_latch = (read_latch & ~0x1ffff) | bitfield(v.alu().fc(), 0, 17); + break; + case 2: // LVOL (Left Volume) + read_latch = (read_latch & ~0xffff) | bitfield(v.lvol(), 0, 16); + break; + case 3: // LVRAMP (Left Volume Ramp) + read_latch = (read_latch & ~0xff00) | (bitfield(v.lvramp(), 0, 8) << 8); + break; + case 4: // RVOL (Right Volume) + read_latch = (read_latch & ~0xffff) | bitfield(v.rvol(), 0, 16); + break; + case 5: // RVRAMP (Right Volume Ramp) + read_latch = (read_latch & ~0xff00) | (bitfield(v.rvramp(), 0, 8) << 8); + break; + case 6: // ECOUNT (Envelope Counter) + read_latch = (read_latch & ~0x01ff) | bitfield(v.ecount(), 0, 9); + break; + case 7: // K2 (Filter Cutoff Coefficient #2) + read_latch = (read_latch & ~0xffff) | bitfield(v.filter().k2(), 0, 16); + break; + case 8: // K2RAMP (Filter Cutoff Coefficient #2 Ramp) + read_latch = (read_latch & ~0xff01) | + (bitfield(v.k2ramp().ramp(), 0, 8) << 8) | + (v.k2ramp().slow() ? 0x0001 : 0x0000); + break; + case 9: // K1 (Filter Cutoff Coefficient #1) + read_latch = (read_latch & ~0xffff) | bitfield(v.filter().k1(), 0, 16); + break; + case 10: // K1RAMP (Filter Cutoff Coefficient #1 Ramp) + read_latch = (read_latch & ~0xff01) | + (bitfield(v.k1ramp().ramp(), 0, 8) << 8) | + (v.k1ramp().slow() ? 0x0001 : 0x0000); + break; + case 11: // ACT (Number of voices) + read_latch = (read_latch & ~0x1f) | bitfield(m_active, 0, 5); + break; + case 12: // MODE (Global Mode) + read_latch = + (read_latch & ~0x1f) | (m_mode.lrclk_en() ? 0x01 : 0x00) | + (m_mode.wclk_en() ? 0x02 : 0x00) | (m_mode.bclk_en() ? 0x04 : 0x00) | + (m_mode.master() ? 0x08 : 0x00) | (m_mode.dual() ? 0x10 : 0x00); + break; + } + } + } + } + + return read_latch; +} + +void es5506_core::regs_w(u8 page, u8 address, u32 data, bool cpu_access) +{ + // Global registers + if (address >= 13) + { + switch (address) + { + case 13: // POT (Pot A/D Register) + // Read only + break; + case 14: // IRQV (Interrupting voice vector) + // Read only + break; + case 15: // PAGE (Page select register) + m_page = bitfield(data, 0, 7); + break; + } + } + else + { + // Channel registers are Write only, and for test purposes + if (bitfield(page, 6)) + { + switch (address) + { + case 0: // CH0L (Channel 0 Left) + case 2: // CH1L (Channel 1 Left) + case 4: // CH2L (Channel 2 Left) + case 6: // CH3L (Channel 3 Left) + case 8: // CH4L (Channel 4 Left) + case 10: // CH5L (Channel 5 Left) + m_ch[bitfield(address, 1, 3)].set_left( + sign_ext(bitfield(data, 0, 23), 23)); + break; + case 1: // CH0R (Channel 0 Right) + case 3: // CH1R (Channel 1 Right) + case 5: // CH2R (Channel 2 Right) + case 7: // CH3R (Channel 3 Right) + case 9: // CH4R (Channel 4 Right) + case 11: // CH5R (Channel 5 Right) + m_ch[bitfield(address, 1, 3)].set_right( + sign_ext(bitfield(data, 0, 23), 23)); + break; + } + } + else + { + const u8 voice = bitfield(page, 0, 5); // Voice select + voice_t &v = m_voice[voice]; + if (bitfield(page, 5)) // Page 32 - 63 + { + switch (address) + { + case 0: // CR (Control Register) + v.alu().set_stop(bitfield(data, 0, 2)); + v.alu().set_lei(bitfield(data, 2)); + v.alu().set_loop(bitfield(data, 3, 2)); + v.alu().set_irqe(bitfield(data, 5)); + v.alu().set_dir(bitfield(data, 6)); + v.alu().set_irq(bitfield(data, 7)); + v.filter().set_lp(bitfield(data, 8, 2)); + v.cr().set_ca(std::min(5, bitfield(data, 10, 3))); + v.cr().set_cmpd(bitfield(data, 13)); + v.cr().set_bs(bitfield(data, 14, 2)); + break; + case 1: // START (Loop Start Register) + v.alu().set_start(data & 0xfffff800); + break; + case 2: // END (Loop End Register) + v.alu().set_end(data & 0xffffff80); + break; + case 3: // ACCUM (Accumulator Register) + v.alu().set_accum(data); + break; + case 4: // O4(n-1) (Filter 4 Temp Register) + v.filter().set_o4_1(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 5: // O3(n-2) (Filter 3 Temp Register #2) + v.filter().set_o3_2(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 6: // O3(n-1) (Filter 3 Temp Register #1) + v.filter().set_o3_1(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 7: // O2(n-2) (Filter 2 Temp Register #2) + v.filter().set_o2_2(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 8: // O2(n-1) (Filter 2 Temp Register #1) + v.filter().set_o2_1(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 9: // O1(n-1) (Filter 1 Temp Register) + v.filter().set_o1_1(sign_ext(bitfield(data, 0, 18), 18)); + break; + case 10: // W_ST (Word Clock Start Register) + m_w_st = bitfield(data, 0, 7); + break; + case 11: // W_END (Word Clock End Register) + m_w_end = bitfield(data, 0, 7); + break; + case 12: // LR_END (Left/Right Clock End Register) + m_lr_end = bitfield(data, 0, 7); + m_lrclk.set_width(m_lr_end); + break; + } + } + else // Page 0 - 31 + { + switch (address) + { + case 0: // CR (Control Register) + v.alu().set_stop(bitfield(data, 0, 2)); + v.alu().set_lei(bitfield(data, 2)); + v.alu().set_loop(bitfield(data, 3, 2)); + v.alu().set_irqe(bitfield(data, 5)); + v.alu().set_dir(bitfield(data, 6)); + v.alu().set_irq(bitfield(data, 7)); + v.filter().set_lp(bitfield(data, 8, 2)); + v.cr().set_ca(std::min(5, bitfield(data, 10, 3))); + v.cr().set_cmpd(bitfield(data, 13)); + v.cr().set_bs(bitfield(data, 14, 2)); + break; + case 1: // FC (Frequency Control) + v.alu().set_fc(bitfield(data, 0, 17)); + break; + case 2: // LVOL (Left Volume) + v.set_lvol(bitfield(data, 0, 16)); + break; + case 3: // LVRAMP (Left Volume Ramp) + v.set_lvramp(bitfield(data, 8, 8)); + break; + case 4: // RVOL (Right Volume) + v.set_rvol(bitfield(data, 0, 16)); + break; + case 5: // RVRAMP (Right Volume Ramp) + v.set_rvramp(bitfield(data, 8, 8)); + break; + case 6: // ECOUNT (Envelope Counter) + v.set_ecount(bitfield(data, 0, 9)); + break; + case 7: // K2 (Filter Cutoff Coefficient #2) + v.filter().set_k2(bitfield(data, 0, 16)); + break; + case 8: // K2RAMP (Filter Cutoff Coefficient #2 Ramp) + v.k2ramp().write(data); + break; + case 9: // K1 (Filter Cutoff Coefficient #1) + v.filter().set_k1(bitfield(data, 0, 16)); + break; + case 10: // K1RAMP (Filter Cutoff Coefficient #1 Ramp) + v.k1ramp().write(data); + break; + case 11: // ACT (Number of voices) + m_active = std::max(4, bitfield(data, 0, 5)); + break; + case 12: // MODE (Global Mode) + m_mode.write(data); + break; + } + } + } + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp new file mode 100644 index 000000000..84a6077de --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -0,0 +1,385 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5506 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_ES5506_HPP +#define _VGSOUND_EMU_SRC_ES5506_HPP + +#pragma once + +#include "es550x.hpp" + +// ES5506 specific +class es5506_core : public es550x_shared_core +{ + private: + class output_t : public vgsound_emu_core + { + public: + output_t(s32 left = 0, s32 right = 0) + : vgsound_emu_core("es5506_output") + , m_left(left) + , m_right(right) + { + } + + void reset() + { + m_left = 0; + m_right = 0; + }; + + inline s32 clamp20(s32 in) { return clamp(in, -0x80000, 0x7ffff); } + + inline void clamp20(output_t src) + { + m_left = clamp20(src.left()); + m_right = clamp20(src.right()); + } + + inline void clamp20() + { + m_left = clamp20(m_left); + m_right = clamp20(m_right); + } + + // setters + inline void set_left(s32 left) { m_left = clamp20(left); } + + inline void set_right(s32 right) { m_right = clamp20(right); } + + void serial_in(bool ch, u8 in) + { + if (ch) // Right output + { + m_right = (m_right << 1) | (in ? 1 : 0); + } + else // Left output + { + m_left = (m_left << 1) | (in ? 1 : 0); + } + } + + // getters + inline u32 left() { return m_left; } + + inline u32 right() { return m_right; } + + output_t &operator+=(output_t &src) + { + m_left = clamp20(m_left + src.left()); + m_right = clamp20(m_right + src.right()); + return *this; + } + + output_t &operator=(output_t src) + { + clamp20(src); + return *this; + } + + output_t &operator=(s32 val) + { + m_left = m_right = clamp20(val); + return *this; + } + + output_t &operator>>(s32 shift) + { + m_left >>= shift; + m_right >>= shift; + return *this; + } + + private: + s32 m_left = 0; + s32 m_right = 0; + }; + + // es5506 voice classes + class voice_t : public es550x_voice_t + { + private: + // es5506 Filter ramp class + class filter_ramp_t : public vgsound_emu_core + { + public: + filter_ramp_t() + : vgsound_emu_core("es5506_filter_ramp") + , m_slow(0) + , m_ramp(0) + { + } + + void reset() + { + m_slow = 0; + m_ramp = 0; + }; + + // Setters + inline void write(u16 data) + { + m_slow = data & 1; + m_ramp = (data >> 8) & 0xff; + } + + // Getters + inline bool slow() { return m_slow; } + + inline u16 ramp() { return m_ramp; } + + private: + u16 m_slow : 1; // Slow mode flag + u16 m_ramp = 8; // Ramp value + }; + + public: + // constructor + voice_t(es5506_core &host) + : es550x_voice_t("es5506_voice", 21, 11, true) + , m_host(host) + , m_lvol(0) + , m_rvol(0) + , m_lvramp(0) + , m_rvramp(0) + , m_ecount(0) + , m_k2ramp(filter_ramp_t()) + , m_k1ramp(filter_ramp_t()) + , m_filtcount(0) + , m_ch(output_t()) + , m_mute(false) + { + } + + // internal state + virtual void reset() override; + virtual void fetch(u8 voice, u8 cycle) override; + virtual void tick(u8 voice) override; + + // Setters + inline void set_lvol(s32 lvol) { m_lvol = lvol; } + + inline void set_rvol(s32 rvol) { m_rvol = rvol; } + + inline void set_lvramp(s32 lvramp) { m_lvramp = lvramp; } + + inline void set_rvramp(s32 rvramp) { m_rvramp = rvramp; } + + inline void set_ecount(s16 ecount) { m_ecount = ecount; } + + // Getters + inline s32 lvol() { return m_lvol; } + + inline s32 rvol() { return m_rvol; } + + inline s32 lvramp() { return m_lvramp; } + + inline s32 rvramp() { return m_rvramp; } + + inline s16 ecount() { return m_ecount; } + + inline filter_ramp_t &k2ramp() { return m_k2ramp; } + + inline filter_ramp_t &k1ramp() { return m_k1ramp; } + + output_t &ch() { return m_ch; } + + // for debug/preview only + inline void set_mute(bool mute) { m_mute = mute; } + + inline s32 left_out() { return m_mute ? 0 : m_ch.left(); } + + inline s32 right_out() { return m_mute ? 0 : m_ch.right(); } + + private: + // accessors, getters, setters + s16 decompress(u8 sample); + s32 volume_calc(u16 volume, s32 in); + + // registers + es5506_core &m_host; + // Volume register: 4 bit exponent, 8 bit mantissa + // 4 LSBs are used for fine control of ramp increment for hardware envelope + s32 m_lvol = 0; // Left volume + s32 m_rvol = 0; // Right volume + // Envelope + s32 m_lvramp = 0; // Left volume ramp + s32 m_rvramp = 0; // Righr volume ramp + s16 m_ecount = 0; // Envelope counter + filter_ramp_t m_k2ramp; // Filter coefficient 2 Ramp + filter_ramp_t m_k1ramp; // Filter coefficient 1 Ramp + u8 m_filtcount = 0; // Internal counter for slow mode + output_t m_ch; // channel output + bool m_mute = false; // mute flag (for debug purpose) + }; + + // 5 bit mode + class mode_t : public vgsound_emu_core + { + public: + mode_t() + : vgsound_emu_core("es5506_mode") + , m_lrclk_en(1) + , m_wclk_en(1) + , m_bclk_en(1) + , m_master(0) + , m_dual(0) + { + } + + // internal states + void reset() + { + m_lrclk_en = 1; + m_wclk_en = 1; + m_bclk_en = 1; + m_master = 0; + m_dual = 0; + } + + // accessors + void write(u8 data) + { + m_lrclk_en = (data >> 0) & 1; + m_wclk_en = (data >> 1) & 1; + m_bclk_en = (data >> 2) & 1; + m_master = (data >> 3) & 1; + m_dual = (data >> 4) & 1; + } + + // getters + bool lrclk_en() { return m_lrclk_en; } + + bool wclk_en() { return m_wclk_en; } + + bool bclk_en() { return m_bclk_en; } + + bool master() { return m_master; } + + bool dual() { return m_dual; } + + private: + u8 m_lrclk_en : 1; // Set LRCLK to output + u8 m_wclk_en : 1; // Set WCLK to output + u8 m_bclk_en : 1; // Set BCLK to output + u8 m_master : 1; // Set memory mode to master + u8 m_dual : 1; // Set dual chip config + }; + + public: + // constructor + es5506_core(es550x_intf &intf) + : es550x_shared_core("es5506", 32, intf) + , m_voice{*this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this, + *this, *this, *this, *this, *this, *this, *this, *this, *this, *this} + , m_read_latch(0) + , m_write_latch(0) + , m_w_st(0) + , m_w_end(0) + , m_lr_end(0) + , m_mode(mode_t()) + , m_w_st_curr(0) + , m_w_end_curr(0) + , m_bclk(clock_pulse_t(4, 0)) + , m_lrclk(clock_pulse_t(32, 1)) + , m_wclk(0) + , m_wclk_lr(false) + , m_output_bit(0) + , m_ch{output_t()} + , m_output{output_t()} + , m_output_temp{output_t()} + , m_output_latch{output_t()} + { + } + + // host interface + u8 host_r(u8 address); + void host_w(u8 address, u8 data); + + // internal state + virtual void reset() override; + virtual void tick() override; + + // less cycle accurate, but also less cpu heavy update routine + void tick_perf(); + + // clock outputs + inline bool bclk() { return m_bclk.current_edge(); } + + inline bool bclk_rising_edge() { return m_bclk.rising_edge(); } + + inline bool bclk_falling_edge() { return m_bclk.falling_edge(); } + + // 6 stereo output channels + inline s32 lout(u8 ch) { return m_output[std::min(5, ch & 0x7)].left(); } + + inline s32 rout(u8 ch) { return m_output[std::min(5, ch & 0x7)].right(); } + + //----------------------------------------------------------------- + // + // for preview/debug purpose only, not for serious emulators + // + //----------------------------------------------------------------- + + // bypass chips host interface for debug purpose only + u8 read(u8 address, bool cpu_access = false); + void write(u8 address, u8 data, bool cpu_access = false); + + u32 regs_r(u8 page, u8 address, bool cpu_access = false); + void regs_w(u8 page, u8 address, u32 data, bool cpu_access = false); + + u8 regs8_r(u8 page, u8 address) + { + u8 prev = m_page; + m_page = page; + u8 ret = read(address, false); + m_page = prev; + return ret; + } + + inline void set_mute(u8 ch, bool mute) { m_voice[ch & 0x1f].set_mute(mute); } + + // per-voice outputs + inline s32 voice_lout(u8 voice) { return (voice < 32) ? m_voice[voice].left_out() : 0; } + + inline s32 voice_rout(u8 voice) { return (voice < 32) ? m_voice[voice].right_out() : 0; } + + protected: + virtual inline u8 max_voices() override { return 32; } + + virtual void voice_tick() override; + + private: + std::array m_voice; // 32 voices + + // Host interfaces + u32 m_read_latch = 0; // 32 bit register latch for host read + u32 m_write_latch = 0; // 32 bit register latch for host write + + // Serial register + u8 m_w_st = 0; // Word clock start register + u8 m_w_end = 0; // Word clock end register + u8 m_lr_end = 0; // Left/Right clock end register + mode_t m_mode; // Global mode + + // Serial related stuffs + u8 m_w_st_curr = 0; // Word clock start, current status + u8 m_w_end_curr = 0; // Word clock end register + clock_pulse_t m_bclk; // BCLK clock (CLKIN / 4), freely running clock + clock_pulse_t m_lrclk; // LRCLK + s16 m_wclk = 0; // WCLK + bool m_wclk_lr = false; // WCLK, L/R output select + s8 m_output_bit = 0; // Bit position in output + std::array m_ch; // 6 stereo output channels + std::array m_output; // Serial outputs + std::array m_output_temp; // temporary signal for serial output + std::array m_output_latch; // output latch +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp new file mode 100644 index 000000000..7de7f0497 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp @@ -0,0 +1,34 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504/ES5505/ES5506 emulation core +*/ + +#include "es550x.hpp" + +// Shared functions +void es550x_shared_core::reset() +{ + m_host_intf.reset(); + m_ha = 0; + m_hd = 0; + m_page = 0; + m_irqv.reset(); + m_active = max_voices() - 1; + m_voice_cycle = 0; + m_voice_fetch = 0; + m_voice_update = false; + m_voice_end = false; + m_clkin.reset(); + m_cas.reset(); + m_e.reset(); +} + +void es550x_shared_core::es550x_voice_t::reset() +{ + m_cr.reset(); + m_alu.reset(); + m_filter.reset(); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp new file mode 100644 index 000000000..4457c2440 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp @@ -0,0 +1,610 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504/ES5505/ES5506 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_ES550X_HPP +#define _VGSOUND_EMU_SRC_ES550X_HPP + +#pragma once + +#include "../core/util.hpp" + +// ES5504/ES5505/ES5506 interface +class es550x_intf : public vgsound_emu_core +{ + public: + es550x_intf() + : vgsound_emu_core("es550x_intf") + { + } + + virtual void e_pin(bool state) {} // E output + + virtual void bclk(bool state) {} // BCLK output (serial specific) + + virtual void lrclk(bool state) {} // LRCLK output (serial specific) + + virtual void wclk(bool state) {} // WCLK output (serial specific) + + virtual void irqb(bool state) {} // irqb output + + virtual u16 adc_r() { return 0; } // ADC input + + virtual void adc_w(u16 data) {} // ADC output + + virtual s16 read_sample(u8 voice, u8 bank, u32 address) { return 0; } +}; + +// Shared functions for ES5504/ES5505/ES5506 +class es550x_shared_core : public vgsound_emu_core +{ + friend class es550x_intf; // es550x specific memory interface + + private: + const u8 m_max_voices = 32; + + protected: + // Interrupt bits + class es550x_irq_t : public vgsound_emu_core + { + public: + es550x_irq_t() + : vgsound_emu_core("es550x_irq") + , m_voice(0) + , m_irqb(1) + { + } + + void reset() + { + m_voice = 0; + m_irqb = 1; + } + + // setter + void set(u8 index) + { + m_irqb = 0; + m_voice = index & 0x1f; + } + + void clear() + { + m_irqb = 1; + m_voice = 0; + } + + // getter + inline bool irqb() { return m_irqb; } + + inline u8 voice() { return m_voice; } + + inline u8 get() { return (m_irqb << 7) | (m_voice & 0x1f); } + + u8 m_voice : 5; + u8 m_irqb : 1; + }; + + // Common voice class + class es550x_voice_t : public vgsound_emu_core + { + private: + // Common control bits + class es550x_control_t : public vgsound_emu_core + { + public: + es550x_control_t() + : vgsound_emu_core("es550x_voice_control") + , m_ca(0) + , m_adc(0) + , m_bs(0) + , m_cmpd(0) + { + } + + void reset() + { + m_ca = 0; + m_adc = 0; + m_bs = 0; + m_cmpd = 0; + } + + // setters + inline void set_ca(u8 ca) { m_ca = ca & 0xf; } + + inline void set_adc(bool adc) { m_adc = adc ? 1 : 0; } + + inline void set_bs(u8 bs) { m_bs = bs & 0x3; } + + inline void set_cmpd(bool cmpd) { m_cmpd = cmpd ? 1 : 0; } + + // getters + inline u8 ca() { return m_ca; } + + inline bool adc() { return m_adc; } + + inline u8 bs() { return m_bs; } + + inline bool cmpd() { return m_cmpd; } + + protected: + // Channel assign - + // 4 bit (16 channel or Bank) for ES5504 + // 2 bit (4 stereo channels) for ES5505 + // 3 bit (6 stereo channels) for ES5506 + u8 m_ca : 4; + + // ES5504 Specific + u8 m_adc : 1; // Start ADC + + // ES5505/ES5506 Specific + u8 m_bs : 2; // Bank bit (1 bit for ES5505, 2 bit for ES5506) + u8 m_cmpd : 1; // Use compressed sample format (ES5506) + }; + + // Accumulator + class es550x_alu_t : public vgsound_emu_core + { + public: + es550x_alu_t(u8 integer, u8 fraction, bool transwave) + : vgsound_emu_core("es550x_voice_alu") + , m_integer(integer) + , m_fraction(fraction) + , m_total_bits(integer + fraction) + , m_accum_mask( + u32(std::min(~0, u64(u64(1) << u64(integer + fraction)) - 1))) + , m_transwave(transwave) + , m_fc(0) + , m_start(0) + , m_end(0) + , m_accum(0) + , m_sample({0}) + { + } + + // configurations + const u8 m_integer; + const u8 m_fraction; + const u8 m_total_bits; + const u32 m_accum_mask; + const bool m_transwave; + + // internal states + void reset(); + bool tick(); + + void loop_exec(); + bool busy(); + s32 interpolation(); + u32 get_accum_integer(); + + void irq_exec(es550x_intf &intf, es550x_irq_t &irqv, u8 index); + + void irq_update(es550x_intf &intf, es550x_irq_t &irqv) + { + intf.irqb(irqv.irqb() ? false : true); + } + + // setters + inline void set_stop0(bool stop0) { m_cr.set_stop0(stop0); } + + inline void set_stop1(bool stop1) { m_cr.set_stop1(stop1); } + + inline void set_lpe(bool lpe) { m_cr.set_lpe(lpe); } + + inline void set_ble(bool ble) { m_cr.set_ble(ble); } + + inline void set_irqe(bool irqe) { m_cr.set_irqe(irqe); } + + inline void set_dir(bool dir) { m_cr.set_dir(dir); } + + inline void set_irq(bool irq) { m_cr.set_irq(irq); } + + inline void set_lei(bool lei) { m_cr.set_lei(lei); } + + inline void set_stop(u8 stop) { m_cr.set_stop(stop); } + + inline void set_loop(u8 loop) { m_cr.set_loop(loop); } + + inline void set_fc(u32 fc) { m_fc = fc; } + + inline void set_start(u32 start, u32 mask = ~0) + { + m_start = (m_start & ~mask) | (start & mask); + } + + inline void set_end(u32 end, u32 mask = ~0) + { + m_end = (m_end & ~mask) | (end & mask); + } + + inline void set_accum(u32 accum, u32 mask = ~0) + { + m_accum = (m_accum & ~mask) | (accum & mask); + } + + inline void set_sample(u8 slot, s32 sample) { m_sample[slot & 1] = sample; } + + // getters + inline bool stop0() { return m_cr.stop0(); } + + inline bool stop1() { return m_cr.stop1(); } + + inline bool lpe() { return m_cr.lpe(); } + + inline bool ble() { return m_cr.ble(); } + + inline bool irqe() { return m_cr.irqe(); } + + inline bool dir() { return m_cr.dir(); } + + inline bool irq() { return m_cr.irq(); } + + inline bool lei() { return m_cr.lei(); } + + inline u8 stop() { return m_cr.stop(); } + + inline u8 loop() { return m_cr.loop(); } + + inline u32 fc() { return m_fc; } + + inline u32 start() { return m_start; } + + inline u32 end() { return m_end; } + + inline u32 accum() { return m_accum; } + + inline s32 sample(u8 slot) { return m_sample[slot & 1]; } + + private: + class es550x_alu_cr_t : public vgsound_emu_core + { + public: + es550x_alu_cr_t() + : vgsound_emu_core("es550x_voice_alu_cr") + , m_stop0(0) + , m_stop1(0) + , m_lpe(0) + , m_ble(0) + , m_irqe(0) + , m_dir(0) + , m_irq(0) + , m_lei(0) + { + } + + void reset() + { + m_stop0 = 0; + m_stop1 = 0; + m_lpe = 0; + m_ble = 0; + m_irqe = 0; + m_dir = 0; + m_irq = 0; + m_lei = 0; + } + + // setters + inline void set_stop0(bool stop0) { m_stop0 = stop0 ? 1 : 0; } + + inline void set_stop1(bool stop1) { m_stop1 = stop1 ? 1 : 0; } + + inline void set_lpe(bool lpe) { m_lpe = lpe ? 1 : 0; } + + inline void set_ble(bool ble) { m_ble = ble ? 1 : 0; } + + inline void set_irqe(bool irqe) { m_irqe = irqe ? 1 : 0; } + + inline void set_dir(bool dir) { m_dir = dir ? 1 : 0; } + + inline void set_irq(bool irq) { m_irq = irq ? 1 : 0; } + + inline void set_lei(bool lei) { m_lei = lei ? 1 : 0; } + + inline void set_stop(u8 stop) + { + m_stop0 = (stop >> 0) & 1; + m_stop1 = (stop >> 1) & 1; + } + + inline void set_loop(u8 loop) + { + m_lpe = (loop >> 0) & 1; + m_ble = (loop >> 1) & 1; + } + + // getters + inline bool stop0() { return m_stop0; } + + inline bool stop1() { return m_stop1; } + + inline bool lpe() { return m_lpe; } + + inline bool ble() { return m_ble; } + + inline bool irqe() { return m_irqe; } + + inline bool dir() { return m_dir; } + + inline bool irq() { return m_irq; } + + inline bool lei() { return m_lei; } + + inline u8 stop() { return (m_stop0 << 0) | (m_stop1 << 1); } + + inline u8 loop() { return (m_lpe << 0) | (m_ble << 1); } + + private: + u8 m_stop0 : 1; // Stop with ALU + u8 m_stop1 : 1; // Stop with processor + u8 m_lpe : 1; // Loop enable + u8 m_ble : 1; // Bidirectional loop enable + u8 m_irqe : 1; // IRQ enable + u8 m_dir : 1; // Playback direction + u8 m_irq : 1; // IRQ bit + u8 m_lei : 1; // Loop end ignore (ES5506 specific) + }; + + es550x_alu_cr_t m_cr; + // Frequency - + // 6 integer, 9 fraction for ES5504/ES5505 + // 6 integer, 11 fraction for ES5506 + u32 m_fc = 0; + u32 m_start = 0; // Start register + u32 m_end = 0; // End register + + // Accumulator - + // 20 integer, 9 fraction for ES5504/ES5505 + // 21 integer, 11 fraction for ES5506 + u32 m_accum = 0; + // Samples + std::array m_sample = {0}; + }; + + // Filter + class es550x_filter_t : public vgsound_emu_core + { + public: + es550x_filter_t() + : vgsound_emu_core("es550x_voice_filter") + , m_lp(0) + , m_k2(0) + , m_k1(0) + { + for (std::array &elem : m_o) + { + std::fill(elem.begin(), elem.end(), 0); + } + } + + void reset(); + void tick(s32 in); + + // setters + inline void set_lp(u8 lp) { m_lp = lp & 3; } + + inline void set_k2(s32 k2) { m_k2 = k2; } + + inline void set_k1(s32 k1) { m_k1 = k1; } + + inline void set_o1_1(s32 o1_1) { m_o[1][0] = o1_1; } + + inline void set_o2_1(s32 o2_1) { m_o[2][0] = o2_1; } + + inline void set_o2_2(s32 o2_2) { m_o[2][1] = o2_2; } + + inline void set_o3_1(s32 o3_1) { m_o[3][0] = o3_1; } + + inline void set_o3_2(s32 o3_2) { m_o[3][1] = o3_2; } + + inline void set_o4_1(s32 o4_1) { m_o[4][0] = o4_1; } + + // getters + inline u8 lp() { return m_lp; } + + inline s32 k2() { return m_k2; } + + inline s32 k1() { return m_k1; } + + inline s32 o1_1() { return m_o[1][0]; } + + inline s32 o2_1() { return m_o[2][0]; } + + inline s32 o2_2() { return m_o[2][1]; } + + inline s32 o3_1() { return m_o[3][0]; } + + inline s32 o3_2() { return m_o[3][1]; } + + inline s32 o4_1() { return m_o[4][0]; } + + private: + void lp_exec(s32 coeff, s32 in, s32 out); + void hp_exec(s32 coeff, s32 in, s32 out); + + // Registers + u8 m_lp = 0; // Filter mode + // Filter coefficient registers + // 12 bit for filter calculation, 4 + // LSBs are used for fine control of ramp increment for + // hardware envelope (ES5506) + s32 m_k2 = 0; // Filter coefficient 2 + s32 m_k1 = 0; // Filter coefficient 1 + + // Filter storage registers + std::array, 5> m_o; + }; + + public: + es550x_voice_t(std::string tag, u8 integer, u8 fraction, bool transwave) + : vgsound_emu_core(tag) + , m_cr(es550x_control_t()) + , m_alu(integer, fraction, transwave) + , m_filter(es550x_filter_t()) + { + } + + // internal state + virtual void reset(); + virtual void fetch(u8 voice, u8 cycle) = 0; + virtual void tick(u8 voice) = 0; + + void irq_update(es550x_intf &intf, es550x_irq_t &irqv) + { + m_alu.irq_update(intf, irqv); + } + + // Getters + es550x_control_t &cr() { return m_cr; } + + es550x_alu_t &alu() { return m_alu; } + + es550x_filter_t &filter() { return m_filter; } + + protected: + es550x_control_t m_cr; + es550x_alu_t m_alu; + es550x_filter_t m_filter; + }; + + // Host interfaces + class host_interface_flag_t : public vgsound_emu_core + { + public: + host_interface_flag_t() + : vgsound_emu_core("es550x_host_interface_flag") + , m_host_access(0) + , m_host_access_strobe(0) + , m_rw(0) + , m_rw_strobe(0) + { + } + + // Accessors + void reset() + { + m_host_access = 0; + m_host_access_strobe = 0; + m_rw = 0; + m_rw_strobe = 0; + } + + // Setters + void set_strobe(bool rw) + { + m_rw_strobe = rw ? 1 : 0; + m_host_access_strobe = 1; + } + + void clear_strobe() { m_host_access_strobe = 0; } + + void clear_host_access() { m_host_access = 0; } + + void update_strobe() + { + m_rw = m_rw_strobe; + m_host_access = m_host_access_strobe; + } + + // Getters + bool host_access() { return m_host_access; } + + bool rw() { return m_rw; } + + private: + u8 m_host_access : 1; // Host access trigger + u8 m_host_access_strobe : 1; // Host access strobe + u8 m_rw : 1; // R/W state + u8 m_rw_strobe : 1; // R/W strobe + }; + + public: + // internal state + virtual void reset(); + + virtual void tick() {} + + // clock outputs + inline bool _cas() { return m_cas.current_edge(); } + + inline bool _cas_rising_edge() { return m_cas.rising_edge(); } + + inline bool _cas_falling_edge() { return m_cas.falling_edge(); } + + inline bool e() { return m_e.current_edge(); } + + inline bool e_rising_edge() { return m_e.rising_edge(); } + + inline bool e_falling_edge() { return m_e.falling_edge(); } + + //----------------------------------------------------------------- + // + // for preview/debug purpose only, not for serious emulators + // + //----------------------------------------------------------------- + + // voice cycle + inline u8 voice_cycle() { return m_voice_cycle; } + + // voice update flag + inline bool voice_update() { return m_voice_update; } + + inline bool voice_end() { return m_voice_end; } + + protected: + // constructor + es550x_shared_core(std::string tag, const u8 voice, es550x_intf &intf) + : vgsound_emu_core(tag) + , m_max_voices(voice) + , m_intf(intf) + , m_host_intf(host_interface_flag_t()) + , m_ha(0) + , m_hd(0) + , m_page(0) + , m_irqv(es550x_irq_t()) + , m_active(m_max_voices - 1) + , m_voice_cycle(0) + , m_voice_fetch(0) + , m_voice_update(0) + , m_voice_end(0) + , m_clkin(clock_pulse_t(1, 0)) + , m_cas(clock_pulse_t(2, 1)) + , m_e(clock_pulse_t(4, 0)) + { + } + + // Constants + virtual inline u8 max_voices() { return m_max_voices; } + + // Shared registers, functions + virtual void voice_tick() {} // voice tick + + es550x_intf &m_intf; // es550x specific memory interface + host_interface_flag_t m_host_intf; // Host interface flag + u8 m_ha = 0; // Host address (4 bit) + u16 m_hd = 0; // Host data (16 bit for ES5504/ES5505, 8 bit for ES5506) + u8 m_page = 0; // Page + es550x_irq_t m_irqv; // Voice interrupt vector registers + + // Internal states + u8 m_active = 31; // Activated voices + // -1, ~25 for ES5504, + // ~32 for ES5505/ES5506 + u8 m_voice_cycle = 0; // Voice cycle + u8 m_voice_fetch = 0; // Voice fetch cycle + bool m_voice_update = false; // Voice update flag + bool m_voice_end = false; // End of one voice cycle flag + clock_pulse_t m_clkin; // CLKIN clock + clock_pulse_t m_cas; // /CAS clock (CLKIN / 4), falling edge of + // CLKIN trigger this clock + clock_pulse_t m_e; // E clock (CLKIN / 8), + // falling edge of CLKIN trigger this clock +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp new file mode 100644 index 000000000..f058f0546 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp @@ -0,0 +1,131 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504/ES5505/ES5506 Shared Accumulator emulation core +*/ + +#include "es550x.hpp" + +// Accumulator functions +void es550x_shared_core::es550x_voice_t::es550x_alu_t::reset() +{ + m_cr.reset(); + m_fc = 0; + m_start = 0; + m_end = 0; + m_accum = 0; + m_sample[0] = m_sample[1] = 0; +} + +bool es550x_shared_core::es550x_voice_t::es550x_alu_t::busy() { return m_cr.stop() == 0; } + +bool es550x_shared_core::es550x_voice_t::es550x_alu_t::tick() +{ + if (m_cr.dir()) + { + m_accum -= m_fc; + } + else + { + m_accum += m_fc; + } + + m_accum &= m_accum_mask; + return ((!m_cr.lei()) && + (((m_cr.dir()) && (m_accum < m_start)) || ((!m_cr.dir()) && (m_accum > m_end)))) + ? true + : false; +} + +void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec() +{ + if (m_cr.irqe()) + { // Set IRQ + m_cr.set_irq(true); + } + + if (m_cr.dir()) // Reverse playback + { + if (m_cr.lpe()) // Loop enable + { + if (m_cr.ble()) // Bidirectional + { + m_cr.set_dir(false); + m_accum = m_start + (m_start - m_accum); + } + else + { // Normal + m_accum = m_end - (m_start - m_accum); + } + } + else if (m_cr.ble() && m_transwave) // m_transwave + { + m_cr.set_loop(0); + m_cr.set_lei(true); // Loop end ignore + m_accum = m_end - (m_start - m_accum); + } + else + { // Stop + m_cr.set_stop0(true); + } + } + else + { + if (m_cr.lpe()) // Loop enable + { + if (m_cr.ble()) // Bidirectional + { + m_cr.set_dir(true); + m_accum = m_end - (m_end - m_accum); + } + else + { // Normal + m_accum = (m_accum - m_end) + m_start; + } + } + else if (m_cr.ble() && m_transwave) // m_transwave + { + m_cr.set_loop(0); + m_cr.set_lei(true); // Loop end ignore + m_accum = (m_accum - m_end) + m_start; + } + else + { // Stop + m_cr.set_stop0(true); + } + } +} + +s32 es550x_shared_core::es550x_voice_t::es550x_alu_t::interpolation() +{ + // SF = S1 + ACCfr * (S2 - S1) + return m_sample[0] + ((bitfield(m_accum, std::max(0, m_fraction - 9), 9) * + (m_sample[1] - m_sample[0])) >> + 9); +} + +u32 es550x_shared_core::es550x_voice_t::es550x_alu_t::get_accum_integer() +{ + return bitfield(m_accum, m_fraction, m_integer); +} + +void es550x_shared_core::es550x_voice_t::es550x_alu_t::irq_exec(es550x_intf &intf, + es550x_irq_t &irqv, + u8 index) +{ + const bool prev = irqv.irqb(); + if (m_cr.irq()) + { + if (irqv.irqb()) + { + irqv.set(index); + m_cr.set_irq(false); + } + } + if (prev != irqv.irqb()) + { + irq_update(intf, irqv); + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp new file mode 100644 index 000000000..360cab813 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp @@ -0,0 +1,72 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Ensoniq ES5504/ES5505/ES5506 Shared Filter emulation core +*/ + +#include "es550x.hpp" + +// Filter functions +void es550x_shared_core::es550x_voice_t::es550x_filter_t::reset() +{ + m_lp = 0; + m_k2 = 0; + m_k1 = 0; + for (std::array &elem : m_o) + { + std::fill(elem.begin(), elem.end(), 0); + } +} + +void es550x_shared_core::es550x_voice_t::es550x_filter_t::tick(s32 in) +{ + // set sample input + m_o[0][0] = in; + + s32 coeff_k1 = s32(bitfield(m_k1, 4, 12)); // 12 MSB used + s32 coeff_k2 = s32(bitfield(m_k2, 4, 12)); // 12 MSB used + + // First and second stage: LP/K1, LP/K1 Fixed + lp_exec(coeff_k1, 0, 1); + lp_exec(coeff_k1, 1, 2); + switch (m_lp) + { + case 0: // LP3 = 0, LP4 = 0: HP/K2, HP/K2 + default: + hp_exec(coeff_k2, 2, 3); + hp_exec(coeff_k2, 3, 4); + break; + case 1: // LP3 = 0, LP4 = 1: HP/K2, LP/K1 + lp_exec(coeff_k1, 2, 3); + hp_exec(coeff_k2, 3, 4); + break; + case 2: // LP3 = 1, LP4 = 0: LP/K2, LP/K2 + lp_exec(coeff_k2, 2, 3); + lp_exec(coeff_k2, 3, 4); + break; + case 3: // LP3 = 1, LP4 = 1: LP/K2, LP/K1 + lp_exec(coeff_k1, 2, 3); + lp_exec(coeff_k2, 3, 4); + break; + } +} + +void es550x_shared_core::es550x_voice_t::es550x_filter_t::lp_exec(s32 coeff, s32 in, s32 out) +{ + // Store previous filter data + m_o[out][1] = m_o[out][0]; + + // Yn = K*(Xn - Yn-1) + Yn-1 + m_o[out][0] = ((coeff * (m_o[in][0] - m_o[out][0])) / 4096) + m_o[out][0]; +} + +void es550x_shared_core::es550x_voice_t::es550x_filter_t::hp_exec(s32 coeff, s32 in, s32 out) +{ + // Store previous filter data + m_o[out][1] = m_o[out][0]; + + // Yn = Xn - Xn-1 + K*Yn-1 + m_o[out][0] = m_o[in][0] - m_o[in][1] + ((coeff * m_o[out][0]) / 8192) + (m_o[out][0] / 2); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/README.md b/vgsound_emu-modified/vgsound_emu/src/k005289/README.md new file mode 100644 index 000000000..cc297b2f4 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k005289/README.md @@ -0,0 +1,23 @@ +# Konami K005289 + +## Summary + +- 2 12 bit timer +- Address generator + +## Source code + +- k005289.hpp: Base header + - k005289.cpp: Source emulation core + +## Description + +This chip is used at infamous Konami Bubble System, for part of Wavetable sound generator. But seriously, It is just to 2 internal 12 bit timer and address generators, rather than sound generator. + +Everything except for internal counter and address are done by external logic, the chip is only has external address, frequency registers and its update pins. + +## Frequency calculation + +``` +Input clock / (4096 - Pitch input) +``` diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp b/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp new file mode 100644 index 000000000..464ce5bc9 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp @@ -0,0 +1,42 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K005289 emulation core +*/ + +#include "k005289.hpp" + +void k005289_core::tick() +{ + for (timer_t &elem : m_timer) + { + elem.tick(); + } +} + +void k005289_core::reset() +{ + for (timer_t &elem : m_timer) + { + elem.reset(); + } +} + +void k005289_core::timer_t::tick() +{ + if (bitfield(++m_counter, 0, 12) == 0) + { + m_addr = bitfield(m_addr + 1, 0, 5); + m_counter = m_freq; + } +} + +void k005289_core::timer_t::reset() +{ + m_addr = 0; + m_pitch = 0; + m_freq = 0; + m_counter = 0; +} diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp b/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp new file mode 100644 index 000000000..6fae1f2cb --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp @@ -0,0 +1,83 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K005289 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_K005289_HPP +#define _VGSOUND_EMU_SRC_K005289_HPP + +#pragma once + +#include "../core/util.hpp" + +class k005289_core : public vgsound_emu_core +{ + private: + // k005289 timer classes + class timer_t : public vgsound_emu_core + { + public: + timer_t() + : vgsound_emu_core("k005289_timer") + , m_addr(0) + , m_pitch(0) + , m_freq(0) + , m_counter(0) + { + } + + // internal state + void reset(); + void tick(); + + // accessors + // Replace current frequency to lastest loaded pitch + inline void update() { m_freq = m_pitch; } + + // setters + // Load pitch data (address pin) + inline void load(u16 pitch) { m_pitch = pitch; } + + // getters + inline u8 addr() { return m_addr; } + + private: + // registers + u8 m_addr = 0; // external address pin + u16 m_pitch = 0; // pitch + u16 m_freq = 0; // current frequency + s16 m_counter = 0; // frequency counter + }; + + public: + // constructor + k005289_core() + : vgsound_emu_core("k005289") + , m_timer{timer_t()} + { + } + + // internal state + void reset(); + void tick(); + + // accessors + // TG1/2 pin + inline void update(int voice) { m_timer[voice & 1].update(); } + + // setters + // LD1/2 pin, A0...11 pin + inline void load(int voice, u16 addr) { m_timer[voice & 1].load(addr); } + + // getters + // 1QA...E/2QA...E pin + inline u8 addr(int voice) { return m_timer[voice & 1].addr(); } + + private: + std::array m_timer; +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/README.md b/vgsound_emu-modified/vgsound_emu/src/k007232/README.md new file mode 100644 index 000000000..790d31077 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k007232/README.md @@ -0,0 +1,75 @@ +# Konami K007232 + +## Summary + +- 2 voice PCM + - 7 bit unsigned, with end marker + - total accessible memory: 128 Kbyte per bank + - Per-voice bankswitchable via E clock +- External 8 bit I/O (usually for volume) + +## Source code + +- k007232.hpp: Base header + - k007232.cpp: Source emulation core + +## Description + +It's Konami's one of custom PCM sound chip, Used at their arcade hardware at mid-80s to early-90s. + +It has 2 channel of PCM, these are has its own output pins...just 7 LSB of currently fetched data. + +PCM Sample format is unique, 1 MSB is end marker and 7 LSB is actually output. (unsigned format) + +The chip itself is DACless, so Sound output and mixing control needs external logics and sound DAC. + +## Register layout + +``` + Address Bits R/W Description + 7654 3210 + + 0x0 xxxx xxxx W Channel 0 Pitch bit 0-7 + 0x1 --x- ---- W Channel 0 4 bit Frequency mode + ---x ---- W Channel 0 8 bit Frequency mode + ---- xxxx W Channel 0 Pitch bit 8-11 + + 0x2 xxxx xxxx W Channel 0 Start address bit 0-7 + 0x3 xxxx xxxx W Channel 0 Start address bit 8-15 + 0x4 ---- ---x W Channel 0 Start address bit 16 + + 0x5 R/W Channel 0 Key on trigger (R/W) + + 0x6 xxxx xxxx W Channel 1 Pitch bit 0-7 + 0x7 --x- ---- W Channel 1 4 bit Frequency mode + ---x ---- W Channel 1 8 bit Frequency mode + ---- xxxx W Channel 1 Pitch bit 8-11 + + 0x8 xxxx xxxx W Channel 1 Start address bit 0-7 + 0x9 xxxx xxxx W Channel 1 Start address bit 8-15 + 0xa ---- ---x W Channel 1 Start address bit 16 + + 0xb R/W Channel 1 Key on trigger (R/W) + + 0xc xxxx xxxx W External port write (w/SLEV pin, Usually for volume control) + + 0xd ---- --x- W Channel 1 Loop enable + ---- ---x W Channel 0 Loop enable +``` + +## Frequency calculation + +(Guesswork in 8/4 bit Frequency mode) + +``` + if 8 bit Frequency mode then + Frequency: Input clock / 4 * (256 - (Pitch bit 0 - 7)) + else if 4 bit Frequency mode then + Frequency: Input clock / 4 * (16 - (Pitch bit 8 - 11)) + else + Frequency: Input clock / 4 * (4096 - (Pitch bit 0 - 11)) +``` + +## Reference + + diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp b/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp new file mode 100644 index 000000000..3798f4c81 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp @@ -0,0 +1,169 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K007232 core +*/ + +#include "k007232.hpp" + +void k007232_core::tick() +{ + for (int i = 0; i < 2; i++) + { + m_voice[i].tick(i); + } +} + +void k007232_core::voice_t::tick(u8 ne) +{ + if (m_busy) + { + const bool is4bit = bitfield(m_pitch, 13); // 4 bit frequency divider flag + const bool is8bit = bitfield(m_pitch, 12); // 8 bit frequency divider flag + + // update counter + if (is4bit) + { + m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) + 1, 0, 8) << 0); + m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) + 1, 0, 4) << 8); + } + else + { + m_counter++; + } + + // handle counter carry + bool carry = + is8bit ? (bitfield(m_counter, 0, 8) == 0) + : (is4bit ? (bitfield(m_counter, 8, 4) == 0) : (bitfield(m_counter, 0, 12) == 0)); + if (carry) + { + m_counter = bitfield(m_pitch, 0, 12); + if (is4bit) // 4 bit frequency has different behavior for address + { + m_addr = (m_addr & ~0x0000f) | (bitfield(bitfield(m_addr, 0, 4) + 1, 0, 4) << 0); + m_addr = (m_addr & ~0x000f0) | (bitfield(bitfield(m_addr, 4, 4) + 1, 0, 4) << 4); + m_addr = (m_addr & ~0x00f00) | (bitfield(bitfield(m_addr, 8, 4) + 1, 0, 4) << 8); + m_addr = (m_addr & ~0x1f000) | (bitfield(bitfield(m_addr, 12, 5) + 1, 0, 5) << 12); + } + else + { + m_addr = bitfield(m_addr + 1, 0, 17); + } + } + + m_data = m_host.m_intf.read_sample(ne, bitfield(m_addr, 0, 17)); // fetch ROM + if (bitfield(m_data, 7)) // check end marker + { + if (m_loop) + { + m_addr = m_start; + } + else + { + m_busy = false; + } + } + + m_out = s8(m_data) - 0x40; // send to output (ASD/BSD) pin + } + else + { + m_out = 0; + } +} + +void k007232_core::write(u8 address, u8 data) +{ + address &= 0xf; // 4 bit for CPU write + + switch (address) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: // voice 0 + case 0x6: + case 0x7: + case 0x8: + case 0x9: + case 0xa: + case 0xb: // voice 1 + m_voice[(address / 6) & 1].write(address % 6, data); + break; + case 0xc: // external register with SLEV pin + m_intf.write_slev(data); + break; + case 0xd: // loop flag + m_voice[0].set_loop(bitfield(data, 0)); + m_voice[1].set_loop(bitfield(data, 1)); + break; + default: break; + } + + m_reg[address] = data; +} + +// write registers on each voices +void k007232_core::voice_t::write(u8 address, u8 data) +{ + switch (address) + { + case 0: // pitch LSB + m_pitch = (m_pitch & ~0x00ff) | data; + break; + case 1: // pitch MSB, divider + m_pitch = (m_pitch & ~0x3f00) | (u16(bitfield(data, 0, 6)) << 8); + break; + case 2: // start address bit 0-7 + m_start = (m_start & ~0x000ff) | data; + break; + case 3: // start address bit 8-15 + m_start = (m_start & ~0x0ff00) | (u32(data) << 8); + break; + case 4: // start address bit 16 + m_start = (m_start & ~0x10000) | (u32(bitfield(data, 16)) << 16); + break; + case 5: // keyon trigger + keyon(); + break; + } +} + +// key on trigger (write OR read 0x05/0x11 register) +void k007232_core::voice_t::keyon() +{ + m_busy = true; + m_counter = bitfield(m_pitch, 0, 12); + m_addr = m_start; +} + +// reset chip +void k007232_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_intf.write_slev(0); + + std::fill(m_reg.begin(), m_reg.end(), 0); +} + +// reset voice +void k007232_core::voice_t::reset() +{ + m_busy = false; + m_loop = false; + m_pitch = 0; + m_start = 0; + m_counter = 0; + m_addr = 0; + m_data = 0; + m_out = 0; +} diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp b/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp new file mode 100644 index 000000000..d500f091a --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp @@ -0,0 +1,115 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K007232 core +*/ + +#ifndef _VGSOUND_EMU_SRC_K007232_HPP +#define _VGSOUND_EMU_SRC_K007232_HPP + +#pragma once + +#include "../core/util.hpp" + +class k007232_intf : public vgsound_emu_core +{ + public: + k007232_intf() + : vgsound_emu_core("k007232_intf") + { + } + + // NE pin is executing voice number, and used for per-voice sample bank. + virtual u8 read_sample(u8 ne, u32 address) { return 0; } + + // SLEV pin actived when 0x0c register accessed + virtual void write_slev(u8 out) {} +}; + +class k007232_core : public vgsound_emu_core +{ + friend class k007232_intf; // k007232 specific interface + + private: + class voice_t : public vgsound_emu_core + { + public: + // constructor + voice_t(k007232_core &host) + : vgsound_emu_core("k007232_voice") + , m_host(host) + , m_busy(false) + , m_loop(false) + , m_pitch(0) + , m_start(0) + , m_counter(0) + , m_addr(0) + , m_data(0) + , m_out(0) + { + } + + // internal state + void reset(); + void tick(u8 ne); + + // accessors + void write(u8 address, u8 data); + void keyon(); + + // setters + inline void set_loop(bool loop) { m_loop = loop; } + + // getters + inline s8 out() { return m_out; } + + private: + // registers + k007232_core &m_host; + bool m_busy = false; // busy status + bool m_loop = false; // loop flag + u16 m_pitch = 0; // pitch, frequency divider + u32 m_start = 0; // start position when keyon or loop start position at + // when reach end marker if loop enabled + u16 m_counter = 0; // frequency counter + u32 m_addr = 0; // current address + u8 m_data = 0; // current data + s8 m_out = 0; // current output (7 bit unsigned) + }; + + public: + // constructor + k007232_core(k007232_intf &intf) + : vgsound_emu_core("k007232") + , m_voice{*this, *this} + , m_intf(intf) + , m_reg{0} + { + } + + // host accessors + void keyon(u8 voice) { m_voice[voice & 1].keyon(); } + + void write(u8 address, u8 data); + + // internal state + void reset(); + void tick(); + + // output for each voices, ASD/BSD pin + inline s32 output(u8 voice) { return m_voice[voice & 1].out(); } + + // getters for debug, trackers, etc + inline u8 reg_r(u8 address) { return m_reg[address & 0xf]; } + + private: + std::array m_voice; + + k007232_intf &m_intf; // common memory interface + + std::array m_reg = {0}; // register pool +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/README.md b/vgsound_emu-modified/vgsound_emu/src/k053260/README.md new file mode 100644 index 000000000..e34947f97 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k053260/README.md @@ -0,0 +1,109 @@ +# Konami K053260 + +## Summary + +- 4 voice DPCM or PCM + - 8 bit signed PCM or 4 bit DPCM (unique type) + - total accessible memory: 2 MByte, 64 KByte per sample +- CPU to CPU Communication +- CPU can be accessible k053260 memory through voice register +- 2 Stereo sound input, 1 Stereo output (YM3012 DAC compatible) + +## Source code + +- k053260.hpp: Base header + - k053260.cpp: Source emulation core + +## Description + +It's one of Konami's custom PCM playback chip with CPU to CPU communication feature, and built in timer. + +It's architecture is successed from K007232, but it features various enhancements: + +it's expanded to 4 channels, Supports more memory space, 4 bit DPCM, Built in volume and stereo panning support, and Dual chip configurations. + +There's 2 stereo inputs and single stereo output, Both format is YM3012 compatible. + +## Register layout + +``` +Address Bits R/W Description + 7654 3210 + +00...03 Communication Registers + +00 xxxx xxxx R Answer from host CPU LSB +01 xxxx xxxx R Answer from host CPU MSB + +02 xxxx xxxx W Reply to host CPU LSB +03 xxxx xxxx W Reply to host CPU MSB + +08...0f Voice 0 Register + +08 xxxx xxxx W Pitch bit 0-7 +09 ---- xxxx W Pitch bit 8-11 + +0a xxxx xxxx W Source length bit 0-7 (byte wide) +0b xxxx xxxx W Source length bit 8-15 (byte wide) + +0c xxxx xxxx W Start address/ROM readback base bit 0-7 +0d xxxx xxxx W Start address/ROM readback base bit 8-15 +0e ---x xxxx W Start address/ROM readback base bit 16-20 + +0f -xxx xxxx W Volume + +10...17 Voice 1 Register +18...1f Voice 2 Register +20...27 Voice 3 Register + +28 ---- x--- W Voice 3 Key on/off trigger + ---- -x-- W Voice 2 Key on/off trigger + ---- --x- W Voice 1 Key on/off trigger + ---- ---x W Voice 0 Key on/off trigger + +29 ---- x--- R Voice 3 busy + ---- -x-- R Voice 2 busy + ---- --x- R Voice 1 busy + ---- ---x R Voice 0 busy + +2a x--- ---- W Voice 3 source format + 0--- ---- 8 bit signed PCM + 1--- ---- 4 bit ADPCM + -x-- ---- W Voice 2 source format + --x- ---- W Voice 1 source format + ---x ---- W Voice 0 source format + + ---- x--- W Voice 3 Loop enable + ---- -x-- W Voice 2 Loop enable + ---- --x- W Voice 1 Loop enable + ---- ---x W Voice 0 Loop enable + +2c --xx x--- W Voice 1 Pan angle in degrees*1 + --00 0--- Mute + --00 1--- 0 degrees + --01 0--- 24 degrees + --01 1--- 35 degrees + --10 0--- 45 degrees + --10 1--- 55 degrees + --11 0--- 66 degrees + --11 1--- 90 degrees + ---- -xxx W Voice 0 Pan angle in degrees*1 + +2d --xx x--- W Voice 3 Pan angle in degrees*1 + ---- -xxx W Voice 2 Pan angle in degrees*1 + +2e xxxx xxxx R ROM readback (use Voice 0 register) + +2f ---- x--- W AUX2 input enable + ---- -x-- W AUX1 input enable + ---- --x- W Sound enable + ---- ---x W ROM readbank enable + +*1 Actually fomula unknown, Use floating point type until explained that. +``` + +## Frequency calculation + +``` + Frequency: Input clock / (4096 - Pitch) +``` diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp new file mode 100644 index 000000000..6fe759ac2 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp @@ -0,0 +1,290 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K053260 core +*/ + +#include "k053260.hpp" + +void k053260_core::tick() +{ + m_out[0] = m_out[1] = 0; + if (m_ctrl.sound_en()) + { + for (int i = 0; i < 4; i++) + { + m_voice[i].tick(i); + m_out[0] += m_voice[i].out(0); + m_out[1] += m_voice[i].out(1); + } + } + // dac clock (YM3012 format) + u8 dac_clock = m_dac.clock(); + if (bitfield(++dac_clock, 0, 4) == 0) + { + m_intf.write_int(m_dac.state()); + u8 dac_state = m_dac.state(); + if (bitfield(++dac_state, 0) == 0) + { + m_ym3012.tick(bitfield(dac_state, 1), m_out[bitfield(dac_state, 1) ^ 1]); + } + + m_dac.set_state(bitfield(dac_state, 0, 2)); + } + m_dac.set_clock(bitfield(dac_clock, 0, 4)); +} + +void k053260_core::voice_t::tick(u8 ne) +{ + if (m_enable && m_busy) + { + bool update = false; + // update counter + if (bitfield(++m_counter, 0, 12) == 0) + { + if (m_bitpos < 8) + { + m_bitpos += 8; + m_addr = bitfield(m_addr + 1, 0, 21); + m_remain--; + } + if (m_adpcm) + { + m_bitpos -= 4; + update = true; + } + else + { + m_bitpos -= 8; + } + } + m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM + if (update) + { + const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM + if (nibble) + { + m_adpcm_buf += bitfield(nibble, 3) ? s8(0x80 >> bitfield(nibble, 0, 3)) + : (1 << bitfield(nibble - 1, 0, 3)); + } + } + + if (m_remain < 0) // check end flag + { + if (m_loop) + { + m_addr = m_start; + m_adpcm_buf = 0; + } + else + { + m_busy = false; + } + } + // calculate output + s32 output = m_adpcm ? m_adpcm_buf : sign_ext(m_data, 8) * s32(m_volume); + // use math for now; actually fomula unknown + m_out[0] = (m_pan >= 0) ? s32(output * cos(f64(m_pan) * PI / 180)) : 0; + m_out[1] = (m_pan >= 0) ? s32(output * sin(f64(m_pan) * PI / 180)) : 0; + } + else + { + m_out[0] = m_out[1] = 0; + } +} + +u8 k053260_core::read(u8 address) +{ + address &= 0x3f; // 6 bit for CPU read + + switch (address) + { + case 0x0: + case 0x1: // Answer from host + return m_host2snd[address & 1]; + break; + case 0x29: // Voice playing status + return (m_voice[0].busy() ? 0x1 : 0x0) | (m_voice[1].busy() ? 0x2 : 0x0) | + (m_voice[2].busy() ? 0x4 : 0x0) | (m_voice[3].busy() ? 0x8 : 0x0); + case 0x2e: // ROM readback + { + if (!m_ctrl.rom_read()) + { + return 0xff; + } + + const u32 rom_addr = m_voice[0].start() + m_voice[0].length(); + m_voice[0].length_inc(); + return m_intf.read_sample(rom_addr); + } + } + return 0xff; +} + +void k053260_core::write(u8 address, u8 data) +{ + address &= 0x3f; // 6 bit for CPU write + + switch (address) + { + case 0x2: + case 0x3: // Reply to host + m_snd2host[address & 1] = data; + break; + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: // voice 0 + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: // voice 1 + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: // voice 2 + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: // voice 3 + m_voice[bitfield(address - 0x8, 3, 2)].write(bitfield(address, 0, 3), data); + break; + case 0x28: // keyon/off toggle + for (int i = 0; i < 4; i++) + { + if (bitfield(data, i) && (!m_voice[i].enable())) + { // rising edge (keyon) + m_voice[i].keyon(); + } + else if ((!bitfield(data, i)) && m_voice[i].enable()) + { // falling edge (keyoff) + m_voice[i].keyoff(); + } + } + break; + case 0x2a: // loop/adpcm flag + for (int i = 0; i < 4; i++) + { + m_voice[i].set_loop(bitfield(data, i)); + m_voice[i].set_adpcm(bitfield(data, i + 4)); + } + break; + case 0x2c: + m_voice[0].set_pan(bitfield(data, 0, 3)); + m_voice[1].set_pan(bitfield(data, 3, 3)); + break; + case 0x2d: + m_voice[2].set_pan(bitfield(data, 0, 3)); + m_voice[3].set_pan(bitfield(data, 3, 3)); + break; + case 0x2f: m_ctrl.write(data); break; + default: break; + } + + m_reg[address] = data; +} + +// write registers on each voices +void k053260_core::voice_t::write(u8 address, u8 data) +{ + switch (address) + { + case 0: // pitch LSB + m_pitch = (m_pitch & ~0x00ff) | data; + break; + case 1: // pitch MSB + m_pitch = (m_pitch & ~0x0f00) | (u16(bitfield(data, 0, 4)) << 8); + break; + case 2: // source length LSB + m_length = (m_length & ~0x000ff) | data; + break; + case 3: // source length MSB + m_length = (m_length & ~0x0ff00) | (u16(data) << 8); + break; + case 4: // start address bit 0-7 + m_start = (m_start & ~0x0000ff) | data; + break; + case 5: // start address bit 8-15 + m_start = (m_start & ~0x00ff00) | (u32(data) << 8); + break; + case 6: // start address bit 16-20 + m_start = (m_start & ~0x1f0000) | (u32(bitfield(data, 16, 5)) << 16); + break; + case 7: // volume + m_volume = bitfield(data, 0, 7); + break; + } +} + +// key on trigger +void k053260_core::voice_t::keyon() +{ + m_enable = m_busy = 1; + m_counter = bitfield(m_pitch, 0, 12); + m_addr = m_start; + m_remain = m_length; + m_bitpos = 4; + m_adpcm_buf = 0; + std::fill(m_out.begin(), m_out.end(), 0); +} + +// key off trigger +void k053260_core::voice_t::keyoff() { m_enable = m_busy = 0; } + +// reset chip +void k053260_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_intf.write_int(0); + + std::fill(m_host2snd.begin(), m_host2snd.end(), 0); + std::fill(m_snd2host.begin(), m_snd2host.end(), 0); + m_ctrl.reset(); + m_dac.reset(); + + std::fill(m_reg.begin(), m_reg.end(), 0); + std::fill(m_out.begin(), m_out.end(), 0); +} + +// reset voice +void k053260_core::voice_t::reset() +{ + m_enable = 0; + m_busy = 0; + m_loop = 0; + m_adpcm = 0; + m_pitch = 0; + m_start = 0; + m_length = 0; + m_volume = 0; + m_pan = -1; + m_counter = 0; + m_addr = 0; + m_remain = 0; + m_bitpos = 4; + m_data = 0; + m_adpcm_buf = 0; + m_out[0] = m_out[1] = 0; +} diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp new file mode 100644 index 000000000..de39e35ec --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp @@ -0,0 +1,262 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami K053260 core +*/ + +#ifndef _VGSOUND_EMU_SRC_K053260_HPP +#define _VGSOUND_EMU_SRC_K053260_HPP + +#pragma once + +#include "../core/util.hpp" + +class k053260_intf : public vgsound_emu_core +{ + public: + k053260_intf() + : vgsound_emu_core("k053260_intf") + { + } + + virtual u8 read_sample(u32 address) { return 0; } // sample fetch + + virtual void write_int(u8 out) {} // timer interrupt +}; + +class k053260_core : public vgsound_emu_core +{ + friend class k053260_intf; // k053260 specific interface + + private: + const int pan_dir[8] = {-1, 0, 24, 35, 45, 55, 66, 90}; // pan direction + + class voice_t : public vgsound_emu_core + { + public: + // constructor + voice_t(k053260_core &host) + : vgsound_emu_core("k053260_voice") + , m_host(host) + , m_enable(0) + , m_busy(0) + , m_loop(0) + , m_adpcm(0) + , m_pitch(0) + , m_start(0) + , m_length(0) + , m_volume(0) + , m_pan(-1) + , m_counter(0) + , m_addr(0) + , m_remain(0) + , m_bitpos(4) + , m_data(0) + , m_adpcm_buf(0) + , m_out{0} + { + } + + // internal state + void reset(); + void tick(u8 ne); + + // accessors + void write(u8 address, u8 data); + void keyon(); + void keyoff(); + + // setters + inline void set_enable(bool enable) { m_enable = enable ? 1 : 0; } + + inline void set_busy(bool busy) { m_busy = busy ? 1 : 0; } + + inline void set_loop(bool loop) { m_loop = loop ? 1 : 0; } + + inline void set_adpcm(bool adpcm) { m_adpcm = adpcm ? 1 : 0; } + + inline void length_inc() { m_length = (m_length + 1) & 0xffff; } + + inline void set_pan(u8 pan) { m_pan = m_host.pan_dir[pan & 7]; } + + // getters + inline bool enable() { return m_enable; } + + inline bool busy() { return m_busy; } + + inline u32 start() { return m_start; } + + inline u16 length() { return m_length; } + + inline s32 out(u8 ch) { return m_out[ch & 1]; } + + private: + // registers + k053260_core &m_host; + u16 m_enable : 1; // enable flag + u16 m_busy : 1; // busy status + u16 m_loop : 1; // loop flag + u16 m_adpcm : 1; // ADPCM flag + u16 m_pitch : 12; // pitch + u32 m_start = 0; // start position + u16 m_length = 0; // source length + u8 m_volume = 0; // master volume + int m_pan = -1; // master pan + u16 m_counter = 0; // frequency counter + u32 m_addr = 0; // current address + s32 m_remain = 0; // remain for end sample + u8 m_bitpos = 4; // bit position for ADPCM decoding + u8 m_data = 0; // current data + s8 m_adpcm_buf = 0; // ADPCM buffer + std::array m_out = {0}; // current output + }; + + class ctrl_t + { + public: + ctrl_t() + : m_rom_read(0) + , m_sound_en(0) + , m_input_en(0) + { + } + + void reset() + { + m_rom_read = 0; + m_sound_en = 0; + m_input_en = 0; + } + + void write(u8 data) + { + m_rom_read = (data >> 0) & 1; + m_sound_en = (data >> 1) & 1; + m_input_en = (data >> 2) & 3; + } + + // getters + inline bool rom_read() { return m_rom_read; } + + inline bool sound_en() { return m_sound_en; } + + inline u8 input_en() { return m_input_en; } + + private: + u8 m_rom_read : 1; // ROM readback + u8 m_sound_en : 1; // Sound enable + u8 m_input_en : 2; // Input enable + }; + + class ym3012_t + { + public: + ym3012_t() + : m_in{0} + , m_out{0} + { + } + + void reset() + { + std::fill(m_in.begin(), m_in.end(), 0); + std::fill(m_out.begin(), m_out.end(), 0); + } + + void tick(u8 ch, s32 in) + { + m_out[(ch & 1)] = m_in[(ch & 1)]; + m_in[(ch & 1) ^ 1] = in; + } + + private: + std::array m_in = {0}; + std::array m_out = {0}; + }; + + class dac_t + { + public: + dac_t() + : m_clock(0) + , m_state(0) + { + } + + void reset() + { + m_clock = 0; + m_state = 0; + } + + inline void set_clock(u8 clock) { m_clock = clock; } + + inline void set_state(u8 state) { m_state = state; } + + inline u8 clock() { return m_clock; } + + inline u8 state() { return m_state; } + + private: + u8 m_clock : 4; // DAC clock (16 clock) + u8 m_state : 2; // DAC state (4 state - SAM1, SAM2) + }; + + public: + // constructor + k053260_core(k053260_intf &intf) + : vgsound_emu_core("k053260") + , m_voice{*this, *this, *this, *this} + , m_intf(intf) + , m_host2snd{0} + , m_snd2host{0} + , m_ctrl(ctrl_t()) + , m_ym3012(ym3012_t()) + , m_dac(dac_t()) + , m_reg{0} + , m_out{0} + { + } + + // communications + inline u8 snd2host_r(u8 address) { return m_snd2host[address & 1]; } + + inline void host2snd_w(u8 address, u8 data) { m_host2snd[address & 1] = data; } + + // sound accessors + u8 read(u8 address); + void write(u8 address, u8 data); + + // internal state + void reset(); + void tick(); + + // getters for debug, trackers, etc + inline s32 output(u8 ch) { return m_out[ch & 1]; } // output for each channels + + inline u8 reg_r(u8 address) { return m_reg[address & 0x3f]; } + + inline s32 voice_out(u8 voice, u8 ch) + { + return (voice < 4) ? m_voice[voice].out(ch & 1) : 0; + } + + private: + std::array m_voice; + k053260_intf &m_intf; // common memory interface + + std::array m_host2snd = {0}; + std::array m_snd2host = {0}; + + ctrl_t m_ctrl; // chip control + + ym3012_t m_ym3012; // YM3012 output + dac_t m_dac; // YM3012 interface + + std::array m_reg = {0}; // register pool + std::array m_out = {0}; // stereo output +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md b/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md new file mode 100644 index 000000000..626b69187 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md @@ -0,0 +1,67 @@ +# OKI MSM6295 + +## Summary + +- 4 voice Dialogic ADPCM + - total accessible memory: 256 KByte +- Clock divider via SS pin + +## Source code + +- msm6295.hpp: Base header + - msm6295.cpp: Source emulation core + +### Dependencies + +- vox.hpp: Dialogic ADPCM decoder header + - vox.cpp: Dialogic ADPCM decoder source + +## Description + +It is 4 channel ADPCM playback chip from OKI semiconductor. It was becomes de facto standard for ADPCM playback in arcade machine, due to cost performance. + +The chip itself is pretty barebone: there is no "register" in chip. It can't control volume and pitch in currently playing channels, only stopable them. And volume is must be determined at playback start command. + +## Command format + + Playback command (2 byte): + +``` +Byte Bit Description + 76543210 +0 1xxxxxxx Phrase select (Header stored in ROM) +1 x000---- Play channel 4 + 0x00---- Play channel 3 + 00x0---- Play channel 2 + 000x---- Play channel 1 + ----xxxx Volume + ----0000 0.0dB + ----0001 -3.2dB + ----0010 -6.0dB + ----0011 -9.2dB + ----0100 -12.0dB + ----0101 -14.5dB + ----0110 -18.0dB + ----0111 -20.5dB + ----1000 -24.0dB +``` + + Suspend command (1 byte): + +``` +Byte Bit Description + 76543210 +0 0x------ Suspend channel 4 + 0-x----- Suspend channel 3 + 0--x---- Suspend channel 2 + 0---x--- Suspend channel 1 +``` + +## Frequency calculation + +``` + if (SS) then + Frequency = Input clock / 165 + else then + Frequency = Input clock / 132 +``` diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp b/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp new file mode 100644 index 000000000..e09c45d90 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp @@ -0,0 +1,179 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + OKI MSM6295 emulation core +*/ + +#include "msm6295.hpp" + +void msm6295_core::tick() +{ + if (m_counter < 4) + { + m_voice[m_counter].tick(); + m_out_temp += m_voice[m_counter].out(); + } + if ((++m_counter) >= (m_ss ? 5 : 4)) + { + // command handler + if (m_command_pending) + { + if (bitfield(m_command, 7)) // play voice + { + if ((++m_clock) >= 15) + { + if (bitfield(m_next_command, 4, 4) != 0) + { + for (int i = 0; i < 4; i++) + { + if (bitfield(m_next_command, 4 + i)) + { + if (!m_voice[i].busy()) + { + m_voice[i].set_command(m_command); + m_voice[i].set_volume(bitfield(m_next_command, 0, 4)); + } + // voices aren't be playable simultaneously at once + break; + } + } + } + m_command = 0; + m_command_pending = false; + m_clock = 0; + } + } + else if (bitfield(m_next_command, 7)) // select phrase + { + if ((++m_clock) >= 15) + { + m_command = m_next_command; + m_command_pending = false; + m_clock = 0; + } + } + else + { + if (bitfield(m_next_command, 3, 4) != 0) // suspend voices + { + for (int i = 0; i < 4; i++) + { + if (bitfield(m_next_command, 3 + i)) + { + if (m_voice[i].busy()) + { + m_voice[i].set_command(m_next_command); + } + } + } + m_next_command &= ~0x78; + } + m_command_pending = false; + } + } + m_out = m_out_temp; + m_out_temp = 0; + m_counter = 0; + } +} + +void msm6295_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_command = 0; + m_next_command = 0; + m_command_pending = false; + m_clock = 0; + m_counter = 0; + m_out = 0; + m_out_temp = 0; +} + +void msm6295_core::voice_t::tick() +{ + if (!m_busy) + { + if (bitfield(m_command, 7)) + { + // get phrase header (stored in data memory) + const u32 phrase = bitfield(m_command, 0, 7) << 3; + // Start address + m_addr = (bitfield(m_host.m_intf.read_byte(phrase | 0), 0, 2) << 16) | + (m_host.m_intf.read_byte(phrase | 1) << 8) | + (m_host.m_intf.read_byte(phrase | 2) << 0); + // End address + m_end = (bitfield(m_host.m_intf.read_byte(phrase | 3), 0, 2) << 16) | + (m_host.m_intf.read_byte(phrase | 4) << 8) | + (m_host.m_intf.read_byte(phrase | 5) << 0); + m_nibble = 4; // MSB first, LSB second + m_command = 0; + m_busy = true; + vox_decoder_t::reset(); + } + m_out = 0; + } + else + { + // playback + if ((++m_clock) >= 33) + { + bool is_end = (m_command != 0); // suspend + decode(bitfield(m_host.m_intf.read_byte(m_addr), m_nibble, 4)); + if (m_nibble <= 0) + { + m_nibble = 4; + if (++m_addr > m_end) + { + is_end = true; + } + } + else + { + m_nibble -= 4; + } + if (is_end) + { + m_command = 0; + m_busy = false; + } + m_out = (step() * m_volume) >> 7; // scale out to 12 bit output + m_clock = 0; + } + } +} + +void msm6295_core::voice_t::reset() +{ + vox_decoder_t::reset(); + m_clock = 0; + m_busy = false; + m_command = 0; + m_addr = 0; + m_nibble = 0; + m_end = 0; + m_volume = 0; + m_out = 0; + m_mute = false; +} + +// accessors +u8 msm6295_core::busy_r() +{ + return (m_voice[0].busy() ? 0x01 : 0x00) | (m_voice[1].busy() ? 0x02 : 0x00) | + (m_voice[2].busy() ? 0x04 : 0x00) | (m_voice[3].busy() ? 0x08 : 0x00); +} + +void msm6295_core::command_w(u8 data) +{ + if (!m_command_pending) + { + m_next_command = data; + m_command_pending = true; + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp b/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp new file mode 100644 index 000000000..04326ae6d --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp @@ -0,0 +1,142 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + OKI MSM6295 emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_MSM6295_HPP +#define _VGSOUND_EMU_SRC_MSM6295_HPP + +#pragma once + +#include "../core/util.hpp" +#include "../core/vox/vox.hpp" + +class msm6295_core : public vox_core +{ + friend class vgsound_emu_mem_intf; // common memory interface + + private: + // Internal volume table, 9 step + const s32 m_volume_table[9] = {32 /* 0.0dB */, + 22 /* -3.2dB */, + 16 /* -6.0dB */, + 11 /* -9.2dB */, + 8 /* -12.0dB */, + 6 /* -14.5dB */, + 4 /* -18.0dB */, + 3 /* -20.5dB */, + 2 /* -24.0dB */}; // scale out to 5 bit for optimization + + // msm6295 voice classes + class voice_t : vox_decoder_t + { + public: + // constructor + voice_t(msm6295_core &host) + : vox_decoder_t(host, false) + , m_host(host) + , m_clock(0) + , m_busy(false) + , m_command(0) + , m_addr(0) + , m_nibble(0) + , m_end(0) + , m_volume(0) + , m_out(0) + , m_mute(false) + { + } + + // internal state + virtual void reset() override; + void tick(); + + // Setters + inline void set_command(u8 command) { m_command = command; } + + inline void set_volume(s32 volume) + { + m_volume = (volume < 9) ? m_host.m_volume_table[volume] : 0; + } + + inline void set_mute(bool mute) { m_mute = mute; } + + // Getters + inline bool busy() { return m_busy; } + + inline s32 out() { return m_mute ? 0 : m_out; } + + private: + // accessors, getters, setters + // registers + msm6295_core &m_host; // host core + u16 m_clock = 0; // clock counter + bool m_busy = false; // busy status + u8 m_command = 0; // current command + u32 m_addr = 0; // current address + s8 m_nibble = 0; // current nibble + u32 m_end = 0; // end address + s32 m_volume = 0; // volume + s32 m_out = 0; // output + // for preview only + bool m_mute = false; // mute flag + }; + + public: + // constructor + msm6295_core(vgsound_emu_mem_intf &intf) + : vox_core("msm6295") + , m_voice{*this, *this, *this, *this} + , m_intf(intf) + , m_ss(false) + , m_command(0) + , m_next_command(0) + , m_command_pending(false) + , m_clock(0) + , m_counter(0) + , m_out(0) + , m_out_temp(0) + { + } + + // accessors, getters, setters + u8 busy_r(); + void command_w(u8 data); + + inline void ss_w(bool ss) { m_ss = ss; } // SS pin + + // internal state + void reset(); + void tick(); + + inline s32 out() { return m_out; } // built in 12 bit DAC + + // for preview + inline void voice_mute(u8 voice, bool mute) + { + if (voice < 4) + { + m_voice[voice].set_mute(mute); + } + } + + inline s32 voice_out(u8 voice) { return (voice < 4) ? m_voice[voice].out() : 0; } + + private: + std::array m_voice; + vgsound_emu_mem_intf &m_intf; // common memory interface + + bool m_ss = false; // SS pin controls divider, input clock / 33 * (SS ? 5 : 4) + u8 m_command = 0; // Command byte + u8 m_next_command = 0; // Next command + bool m_command_pending = false; // command pending flag + u16 m_clock = 0; // clock counter + u16 m_counter = 0; // another clock counter + s32 m_out = 0; // 12 bit output + s32 m_out_temp = 0; // temporary buffer of above +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/README.md b/vgsound_emu-modified/vgsound_emu/src/n163/README.md new file mode 100644 index 000000000..984356be7 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/n163/README.md @@ -0,0 +1,88 @@ +# Namco 163 + +## Summary + +- 1 to 8 voice wavetable + - 4 bit unsigned + - each waveforms are can be placed to anywhere in internal RAM, and its size is can be variable. +- activated voice count can be changed any time, multiplexed output + +## Source code + +- n163.hpp: Base header + - n163.cpp: Source emulation core + +## Description + +This chip is one of NES mapper with sound expansion, This one is by Namco. + +It has 1 to 8 wavetable channels, All channel registers and waveforms are stored to internal RAM. 4 bit Waveforms are freely allocatable, and its length is variables; its can be stores many short waveforms or few long waveforms in RAM. + +But waveforms are needs to squash, reallocate to avoid conflict with channel register area, each channel register size is 8 bytes per channels. + +Sound output is time division multiplexed, it's can be captured only single channels output at once. in reason, More activated channels are less sound quality. + +## Sound register layout + +``` +Address Bit Description + 7654 3210 + +78-7f Channel 0 +78 xxxx xxxx Channel 0 Pitch input bit 0-7 +79 xxxx xxxx Channel 0 Accumulator bit 0-7 +7a xxxx xxxx Channel 0 Pitch input bit 8-15 +7b xxxx xxxx Channel 0 Accumulator bit 8-15 +7c xxxx xx-- Channel 0 Waveform length, 256 - (x * 4) + ---- --xx Channel 0 Pitch input bit 16-17 +7d xxxx xxxx Channel 0 Accumulator bit 16-23 +7e xxxx xxxx Channel 0 Waveform base offset + xxxx xxx- RAM byte (0 to 127) + ---- ---x RAM nibble + ---- ---0 Low nibble + ---- ---1 High nibble +7f ---- xxxx Channel 0 Volume + +7f Number of active channels +7f -xxx ---- Number of active channels + -000 ---- Channel 0 activated + -001 ---- Channel 1 activated + -010 ---- Channel 2 activated + ... + -110 ---- Channel 6 activated + -111 ---- Channel 7 activated + +70-77 Channel 1 (Optional if activated) +68-6f Channel 2 (Optional if activated) +... +48-4f Channel 6 (Optional if activated) +40-47 Channel 7 (Optional if activated) +``` + +Rest of RAM area are for 4 bit Waveform and/or scratchpad. + +## Waveform format + +Each waveform byte has 2 nibbles packed, fetches LSB first, MSB next. + +``` + ---- xxxx 4 bit waveform, LSB + xxxx ---- Same as above, MSB +``` + +Waveform address: Waveform base offset + Bit 16 to 23 of Accumulator, 1 LSB of result is nibble select, 7 MSB of result is Byte address in RAM. + +## Frequency calculation + +``` + Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536) +``` + +## Technical notice + +This core only outputs raw output from chip (or accumulated output, see below); any kind of off-chip stuff needs to implemented outside core. + +There's to way for reduce N163 noises: reduce channel limit and demultiplex: + +- Channel limit is runtime changeable and it makes some usable effects. +- Demultiplex is used for "non-ear destroyable" emulators, but less hardware accurate. (when LPF and RF filter is not considered) This core is support both, You can choose output behavior diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp b/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp new file mode 100644 index 000000000..783bf2df3 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp @@ -0,0 +1,120 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Namco 163 Sound emulation core +*/ + +#include "n163.hpp" + +void n163_core::tick() +{ + if (m_multiplex) + { + m_out = 0; + } + // 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank + // 0x8000-0x9fff select.) + if (m_disable) + { + if (!m_multiplex) + { + m_out = 0; + } + return; + } + + // tick per each clock + const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | + (bitfield(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency + + u32 accum = m_ram[m_voice_cycle + 1] | (u32(m_ram[m_voice_cycle + 3]) << 8) | + (u32(m_ram[m_voice_cycle + 5]) << 16); // 24 bit accumulator + + const u16 length = 256 - (m_ram[m_voice_cycle + 4] & 0xfc); + const u8 addr = m_ram[m_voice_cycle + 6] + bitfield(accum, 16, 8); + const s16 wave = (bitfield(m_ram[bitfield(addr, 1, 7)], bitfield(addr, 0) << 2, 4) - 8); + const s16 volume = bitfield(m_ram[m_voice_cycle + 7], 0, 4); + + // get per-voice output + const s16 voice_out = (wave * volume); + m_voice_out[(m_voice_cycle >> 3) & 7] = voice_out; + + // accumulate address + accum = bitfield(accum + freq, 0, 24); + if (bitfield(accum, 16, 8) >= length) + { + accum = bitfield(accum, 0, 18); + } + + // writeback to register + m_ram[m_voice_cycle + 1] = bitfield(accum, 0, 8); + m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8); + m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8); + + // update voice cycle + bool flush = m_multiplex ? true : false; + m_voice_cycle -= 0x8; + if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3))) + { + if (!m_multiplex) + { + flush = true; + } + m_voice_cycle = 0x78; + } + + // output 4 bit waveform and volume, multiplexed + m_acc += voice_out; + if (flush) + { + m_out = m_acc / (m_multiplex ? 1 : (bitfield(m_ram[0x7f], 4, 3) + 1)); + m_acc = 0; + } +} + +void n163_core::reset() +{ + // reset this chip + m_disable = false; + m_multiplex = true; + std::fill(m_ram.begin(), m_ram.end(), 0); + m_voice_cycle = 0x78; + m_addr_latch.reset(); + m_out = 0; + m_acc = 0; +} + +// accessor +void n163_core::addr_w(u8 data) +{ + // 0xf800-0xffff Sound address, increment + m_addr_latch.write(data); +} + +void n163_core::data_w(u8 data, bool cpu_access) +{ + // 0x4800-0x4fff Sound data write + m_ram[m_addr_latch.addr()] = data; + + // address latch increment + if (cpu_access && m_addr_latch.incr()) + { + m_addr_latch.addr_inc(); + } +} + +u8 n163_core::data_r(bool cpu_access) +{ + // 0x4800-0x4fff Sound data read + const u8 ret = m_ram[m_addr_latch.addr()]; + + // address latch increment + if (cpu_access && m_addr_latch.incr()) + { + m_addr_latch.addr_inc(); + } + + return ret; +} diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp b/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp new file mode 100644 index 000000000..1c04d1f36 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp @@ -0,0 +1,109 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Namco 163 Sound emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_N163_HPP +#define _VGSOUND_EMU_SRC_N163_HPP + +#pragma once + +#include "../core/util.hpp" + +class n163_core : public vgsound_emu_core +{ + private: + // Address latch + class addr_latch_t : public vgsound_emu_core + { + public: + addr_latch_t() + : vgsound_emu_core("namco_163_addr_latch") + , m_addr(0) + , m_incr(0) + { + } + + void reset() + { + m_addr = 0; + m_incr = 0; + } + + // accessors + inline void write(u8 data) + { + m_addr = (data >> 0) & 0x7f; + m_incr = (data >> 7) & 0x01; + } + + inline void addr_inc() { m_addr = (m_addr + 1) & 0x7f; } + + // getters + inline u8 addr() { return m_addr; } + + inline bool incr() { return m_incr; } + + private: + u8 m_addr : 7; + u8 m_incr : 1; + }; + + public: + n163_core() + : vgsound_emu_core("namco_163") + , m_disable(false) + , m_ram{0} + , m_voice_cycle(0x78) + , m_addr_latch(addr_latch_t()) + , m_out(0) + , m_voice_out{0} + , m_multiplex(true) + , m_acc(0) + { + } + + // accessors, getters, setters + void addr_w(u8 data); + void data_w(u8 data, bool cpu_access = false); + u8 data_r(bool cpu_access = false); + + inline void set_disable(bool disable) { m_disable = disable; } + + // internal state + void reset(); + void tick(); + + // sound output pin + inline s16 out() { return m_out; } + + // register pool + inline u8 reg(u8 addr) { return m_ram[addr & 0x7f]; } + + inline void set_multiplex(bool multiplex = true) { m_multiplex = multiplex; } + + // preview only + inline u8 voice_cycle() { return m_voice_cycle; } + + inline s16 voice_out(u8 voice) + { + return (voice <= ((m_ram[0x7f] >> 4) & 7)) ? m_voice_out[7 - voice] : 0; + } + + private: + bool m_disable = false; + std::array m_ram = {0}; // internal 128 byte RAM + u8 m_voice_cycle = 0x78; // Voice cycle for processing + addr_latch_t m_addr_latch; // address latch + s16 m_out = 0; // output + + std::array m_voice_out = {0}; // per-voice output, for preview only + // demultiplex related + bool m_multiplex = true; // multiplex flag, but less noisy = inaccurate! + s16 m_acc = 0; // accumulated output +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/README.md b/vgsound_emu-modified/vgsound_emu/src/scc/README.md new file mode 100644 index 000000000..2a66bd8cb --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/scc/README.md @@ -0,0 +1,314 @@ +# Konami SCC + +## Summary + +- 5 voice wavetable + - 8 bit signed, 32 width long for each voice + - each voice has separated waveform space + - 4th and 5th voice shares waveform (K051649) +- MegaROM mapper (K051649) or MegaRAM mapper (K052539) + +## Source code + +- scc.hpp: Base header + - scc.cpp: Source emulation core + +## Description + +Konami SCC means "Sound Creative Chip", it's actually MSX MegaROM/RAM Mapper with 5 channel Wavetable sound generator. + +It was first appeared at 1987, F-1 Spirit and Nemesis 2/Gradius 2 for MSX. then several MSX cartridges used that until 1990, Metal Gear 2: Solid Snake. Even after MSX is discontinued, it was still used at some low-end arcade and amusement hardwares. and some Third-party MSX utilities still support this due to its market shares. + +There's 2 SCC types: + +- K051649 (or simply known as SCC) + This chip is used for MSX MegaROM Mapper, some arcade machines. + Channel 4 and 5 must be share waveform, other channels has its own waveforms. + +- K052539 (also known as SCC+) + This chip is used for MSX MegaRAM Mapper (Konami Sound Cartridges for Snatcher/SD Snatcher). All channels can be has its own waveforms, and also has backward compatibility mode with K051649. + +## Register layout + +### K051649 + +- 4000-bfff MegaROM Mapper + +``` +Address Bit R/W Description + 7654 3210 + +4000-5fff xxxx xxxx R Bank page 0 +c000-dfff mirror of 4000-5fff + +6000-7fff xxxx xxxx R Bank page 1 +e000-ffff mirror of 6000-7fff + +8000-9fff xxxx xxxx R Bank page 2 +0000-1fff mirror of 8000-9fff + +a000-bfff xxxx xxxx R Bank page 3 +2000-3fff mirror of a000-bfff +``` + +- 5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select + +``` +Address Bit R/W Description + 7654 3210 + +5000 --xx xxxx W Bank select, Page 0 +5001-57ff Mirror of 5000 + +7000 --xx xxxx W Bank select, Page 1 +7001-77ff Mirror of 7000 + +9000 --xx xxxx W Bank select, Page 2 + --11 1111 W SCC Enable +9001-97ff Mirror of 9000 + +b000 --xx xxxx W Bank select, Page 3 +b001-b7ff Mirror of b000 +``` + +- 9800-9fff SCC register + +``` +9800-987f Waveform + +Address Bit R/W Description + 7654 3210 + +9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) +9820-983f xxxx xxxx R/W Channel 1 "" +9840-985f xxxx xxxx R/W Channel 2 "" +9860-987f xxxx xxxx R/W Channel 3/4 "" + +9880-9889 Pitch + +9880 xxxx xxxx W Channel 0 Pitch LSB +9881 ---- xxxx W Channel 0 Pitch MSB +9882 xxxx xxxx W Channel 1 Pitch LSB +9883 ---- xxxx W Channel 1 Pitch MSB +9884 xxxx xxxx W Channel 2 Pitch LSB +9885 ---- xxxx W Channel 2 Pitch MSB +9886 xxxx xxxx W Channel 3 Pitch LSB +9887 ---- xxxx W Channel 3 Pitch MSB +9888 xxxx xxxx W Channel 4 Pitch LSB +9889 ---- xxxx W Channel 4 Pitch MSB + +9888-988e Volume + +988a ---- xxxx W Channel 0 Volume +988b ---- xxxx W Channel 1 Volume +988c ---- xxxx W Channel 2 Volume +988d ---- xxxx W Channel 3 Volume +988e ---- xxxx W Channel 4 Volume + +988f ---x ---- W Channel 4 Output enable/disable flag + ---- x--- W Channel 3 Output enable/disable flag + ---- -x-- W Channel 2 Output enable/disable flag + ---- --x- W Channel 1 Output enable/disable flag + ---- ---x W Channel 0 Output enable/disable flag + +9890-989f Mirror of 9880-988f + +98a0-98bf xxxx xxxx R Channel 4 Waveform + +98e0 x--- ---- W Waveform rotate flag for channel 4 + -x-- ---- W Waveform rotate flag for all channels + --x- ---- W Reset waveform position after pitch writes + ---- --x- W 8 bit frequency + ---- --0x W 4 bit frequency + +98e1-98ff Mirror of 98e0 + +9900-9fff Mirror of 9800-98ff +``` + +### K052539 + +- 4000-bfff MegaRAM Mapper + +``` +Address Bit R/W Description + 7654 3210 + +4000-5fff xxxx xxxx R/W Bank page 0 +c000-dfff xxxx xxxx R/W "" + +6000-7fff xxxx xxxx R/W Bank page 1 +e000-ffff xxxx xxxx R/W "" + +8000-9fff xxxx xxxx R/W Bank page 2 +0000-1fff xxxx xxxx R/W "" + +a000-bfff xxxx xxxx R/W Bank page 3 +2000-3fff xxxx xxxx R/W "" +``` + +- 5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select + +``` +Address Bit R/W Description + 7654 3210 + +5000 xxxx xxxx W Bank select, Page 0 +5001-57ff Mirror of 5000 + +7000 xxxx xxxx W Bank select, Page 1 +7001-77ff Mirror of 7000 + +9000 xxxx xxxx W Bank select, Page 2 + --11 1111 W SCC Enable (SCC Compatible mode) +9001-97ff Mirror of 9000 + +b000 xxxx xxxx W Bank select, Page 3 + 1--- ---- W SCC+ Enable (SCC+ mode) +b001-b7ff Mirror of b000 +``` + +- bffe-bfff Mapper configuration + +``` +Address Bit R/W Description + 7654 3210 + +bffe --x- ---- W SCC operation mode + --0- ---- W SCC Compatible mode + --1- ---- W SCC+ mode + ---x ---- W RAM write/Bank select toggle for all Bank pages + ---0 ---- W Bank select enable + ---1 ---- W RAM write enable + ---0 -x-- W RAM write/Bank select toggle for Bank page 2 + ---0 --x- W RAM write/Bank select toggle for Bank page 1 + ---0 ---x W RAM write/Bank select toggle for Bank page 0 + +bfff Mirror of bffe +``` + +- 9800-9fff SCC Compatible mode register + +``` +9800-987f Waveform + +Address Bit R/W Description + 7654 3210 + +9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) +9820-983f xxxx xxxx R/W Channel 1 "" +9840-985f xxxx xxxx R/W Channel 2 "" +9860-987f xxxx xxxx R/W Channel 3/4 "" + +9880-9889 Pitch + +9880 xxxx xxxx W Channel 0 Pitch LSB +9881 ---- xxxx W Channel 0 Pitch MSB +9882 xxxx xxxx W Channel 1 Pitch LSB +9883 ---- xxxx W Channel 1 Pitch MSB +9884 xxxx xxxx W Channel 2 Pitch LSB +9885 ---- xxxx W Channel 2 Pitch MSB +9886 xxxx xxxx W Channel 3 Pitch LSB +9887 ---- xxxx W Channel 3 Pitch MSB +9888 xxxx xxxx W Channel 4 Pitch LSB +9889 ---- xxxx W Channel 4 Pitch MSB + +9888-988e Volume + +988a ---- xxxx W Channel 0 Volume +988b ---- xxxx W Channel 1 Volume +988c ---- xxxx W Channel 2 Volume +988d ---- xxxx W Channel 3 Volume +988e ---- xxxx W Channel 4 Volume + +988f ---x ---- W Channel 4 Output enable/disable flag + ---- x--- W Channel 3 Output enable/disable flag + ---- -x-- W Channel 2 Output enable/disable flag + ---- --x- W Channel 1 Output enable/disable flag + ---- ---x W Channel 0 Output enable/disable flag + +9890-989f Mirror of 9880-988f + +98a0-98bf xxxx xxxx R Channel 4 Waveform + +98c0 -x-- ---- W Waveform rotate flag for all channels + --x- ---- W Reset waveform position after pitch writes + ---- --x- W 8 bit frequency + ---- --0x W 4 bit frequency + +98c1-98df Mirror of 98c0 + + 9900-9fff Mirror of 9800-98ff +``` + +- b800-bfff SCC+ mode register + +``` +b800-b89f Waveform + +Address Bit R/W Description + 7654 3210 + +b800-b81f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed) +b820-b83f xxxx xxxx R/W Channel 1 "" +b840-b85f xxxx xxxx R/W Channel 2 "" +b860-b87f xxxx xxxx R/W Channel 3 "" +b880-b89f xxxx xxxx R/W Channel 3 "" + +b8a0-b8a9 Pitch + +b8a0 xxxx xxxx W Channel 0 Pitch LSB +b8a1 ---- xxxx W Channel 0 Pitch MSB +b8a2 xxxx xxxx W Channel 1 Pitch LSB +b8a3 ---- xxxx W Channel 1 Pitch MSB +b8a4 xxxx xxxx W Channel 2 Pitch LSB +b8a5 ---- xxxx W Channel 2 Pitch MSB +b8a6 xxxx xxxx W Channel 3 Pitch LSB +b8a7 ---- xxxx W Channel 3 Pitch MSB +b8a8 xxxx xxxx W Channel 4 Pitch LSB +b8a9 ---- xxxx W Channel 4 Pitch MSB + +b8a8-b8ae Volume + +b8aa ---- xxxx W Channel 0 Volume +b8ab ---- xxxx W Channel 1 Volume +b8ac ---- xxxx W Channel 2 Volume +b8ad ---- xxxx W Channel 3 Volume +b8ae ---- xxxx W Channel 4 Volume + +b8af ---x ---- W Channel 4 Output enable/disable flag + ---- x--- W Channel 3 Output enable/disable flag + ---- -x-- W Channel 2 Output enable/disable flag + ---- --x- W Channel 1 Output enable/disable flag + ---- ---x W Channel 0 Output enable/disable flag + +b8b0-b8bf Mirror of b8a0-b8af + +b8c0 -x-- ---- W Waveform rotate flag for all channels + --x- ---- W Reset waveform position after pitch writes + ---- --x- W 8 bit frequency + ---- --0x W 4 bit frequency + +b8c1-b8df Mirror of b8c0 + +b900-bfff Mirror of b800-b8ff +``` + +## Frequency calculation + +``` + if 8 bit frequency then + Frequency = Input clock / ((bit 0 to 7 of Pitch input) + 1) + else if 4 bit frequency then + Frequency = Input clock / ((bit 8 to 11 of Pitch input) + 1) + else + Frequency = Input clock / (Pitch input + 1) +``` + +## Reference + + + + + + diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp b/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp new file mode 100644 index 000000000..07cbb60e8 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp @@ -0,0 +1,461 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst + Konami SCC emulation core +*/ + +#include "scc.hpp" + +// shared SCC features +void scc_core::tick() +{ + m_out = 0; + for (auto &elem : m_voice) + { + elem.tick(); + m_out += elem.out(); + } +} + +void scc_core::voice_t::tick() +{ + if (m_pitch >= 9) // or voice is halted + { + // update counter - Post decrement + const u16 temp = m_counter; + if (m_host.m_test.freq_4bit()) // 4 bit frequency mode + { + m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); + m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); + } + else + { + m_counter = bitfield(m_counter - 1, 0, 12); + } + + // handle counter carry + const bool carry = m_host.m_test.freq_8bit() + ? (bitfield(temp, 0, 8) == 0) + : (m_host.m_test.freq_4bit() ? (bitfield(temp, 8, 4) == 0) + : (bitfield(temp, 0, 12) == 0)); + if (carry) + { + m_addr = bitfield(m_addr + 1, 0, 5); + m_counter = m_pitch; + } + } + // get output + if (m_enable) + { + m_out = (m_wave[m_addr] * m_volume) >> 4; // scale to 11 bit digital output + } + else + { + m_out = 0; + } +} + +void scc_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_test.reset(); + m_out = 0; + std::fill(m_reg.begin(), m_reg.end(), 0); +} + +void scc_core::voice_t::reset() +{ + std::fill(m_wave.begin(), m_wave.end(), 0); + m_enable = false; + m_pitch = 0; + m_volume = 0; + m_addr = 0; + m_counter = 0; + m_out = 0; +} + +// SCC accessors +u8 scc_core::wave_r(bool is_sccplus, u8 address) +{ + u8 ret = 0xff; + const u8 voice = bitfield(address, 5, 3); + if (voice > 4) + { + return ret; + } + + u8 wave_addr = bitfield(address, 0, 5); + + if (m_test.rotate()) + { // rotate flag + wave_addr = bitfield(wave_addr + m_voice[voice].addr(), 0, 5); + } + + if (!is_sccplus) + { + if (voice == 3) // rotate voice 3~4 flag + { + if (m_test.rotate4() || m_test.rotate()) + { // rotate flag + wave_addr = + bitfield(bitfield(address, 0, 5) + m_voice[3 + m_test.rotate()].addr(), 0, 5); + } + } + } + ret = m_voice[voice].wave(wave_addr); + + return ret; +} + +void scc_core::wave_w(bool is_sccplus, u8 address, u8 data) +{ + if (m_test.rotate()) + { // write protected + return; + } + + const u8 voice = bitfield(address, 5, 3); + if (voice > 4) + { + return; + } + + const u8 wave_addr = bitfield(address, 0, 5); + + if (!is_sccplus) + { + if (((voice >= 3) && m_test.rotate4()) || (voice >= 4)) + { // Ignore if write protected, or voice 4 + return; + } + if (voice >= 3) // voice 3, 4 shares waveform + { + m_voice[3].set_wave(wave_addr, data); + m_voice[4].set_wave(wave_addr, data); + } + else + { + m_voice[voice].set_wave(wave_addr, data); + } + } + else + { + m_voice[voice].set_wave(wave_addr, data); + } +} + +void scc_core::freq_vol_enable_w(u8 address, u8 data) +{ + // *0-*f Pitch, Volume, Enable + address = bitfield(address, 0, 4); // mask address to 4 bit + const u8 voice_freq = bitfield(address, 1, 3); + switch (address) + { + case 0x0: // 0x*0 Voice 0 Pitch LSB + case 0x2: // 0x*2 Voice 1 Pitch LSB + case 0x4: // 0x*4 Voice 2 Pitch LSB + case 0x6: // 0x*6 Voice 3 Pitch LSB + case 0x8: // 0x*8 Voice 4 Pitch LSB + if (m_test.resetpos()) + { // Reset address + m_voice[voice_freq].reset_addr(); + } + m_voice[voice_freq].set_pitch(data, 0x0ff); + break; + case 0x1: // 0x*1 Voice 0 Pitch MSB + case 0x3: // 0x*3 Voice 1 Pitch MSB + case 0x5: // 0x*5 Voice 2 Pitch MSB + case 0x7: // 0x*7 Voice 3 Pitch MSB + case 0x9: // 0x*9 Voice 4 Pitch MSB + if (m_test.resetpos()) + { // Reset address + m_voice[voice_freq].reset_addr(); + } + m_voice[voice_freq].set_pitch(u16(bitfield(data, 0, 4)) << 8, 0xf00); + break; + case 0xa: // 0x*a Voice 0 Volume + case 0xb: // 0x*b Voice 1 Volume + case 0xc: // 0x*c Voice 2 Volume + case 0xd: // 0x*d Voice 3 Volume + case 0xe: // 0x*e Voice 4 Volume + m_voice[address - 0xa].set_volume(bitfield(data, 0, 4)); + break; + case 0xf: // 0x*f Enable/Disable flag + m_voice[0].set_enable(bitfield(data, 0)); + m_voice[1].set_enable(bitfield(data, 1)); + m_voice[2].set_enable(bitfield(data, 2)); + m_voice[3].set_enable(bitfield(data, 3)); + m_voice[4].set_enable(bitfield(data, 4)); + break; + } +} + +void k051649_scc_core::scc_w(bool is_sccplus, u8 address, u8 data) +{ + const u8 voice = bitfield(address, 5, 3); + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3/4 Waveform + wave_w(false, address, data); + break; + case 0b100: // 0x80-0x9f Pitch, Volume, Enable + freq_vol_enable_w(address, data); + break; + case 0b111: // 0xe0-0xff Test register + m_test.set_freq_4bit(bitfield(data, 0)); + m_test.set_freq_8bit(bitfield(data, 1)); + m_test.set_resetpos(bitfield(data, 5)); + m_test.set_rotate(bitfield(data, 6)); + m_test.set_rotate4(bitfield(data, 7)); + break; + } + m_reg[address] = data; +} + +void k052539_scc_core::scc_w(bool is_sccplus, u8 address, u8 data) +{ + const u8 voice = bitfield(address, 5, 3); + if (is_sccplus) + { + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3 Waveform + case 0b100: // 0x80-0x9f Voice 4 Waveform + wave_w(true, address, data); + break; + case 0b101: // 0xa0-0xbf Pitch, Volume, Enable + freq_vol_enable_w(address, data); + break; + case 0b110: // 0xc0-0xdf Test register + m_test.set_freq_4bit(bitfield(data, 0)); + m_test.set_freq_8bit(bitfield(data, 1)); + m_test.set_resetpos(bitfield(data, 5)); + m_test.set_rotate(bitfield(data, 6)); + break; + default: break; + } + } + else + { + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3/4 Waveform + wave_w(false, address, data); + break; + case 0b100: // 0x80-0x9f Pitch, Volume, Enable + freq_vol_enable_w(address, data); + break; + case 0b110: // 0xc0-0xdf Test register + m_test.set_freq_4bit(bitfield(data, 0)); + m_test.set_freq_8bit(bitfield(data, 1)); + m_test.set_resetpos(bitfield(data, 5)); + m_test.set_rotate(bitfield(data, 6)); + break; + default: break; + } + } + m_reg[address] = data; +} + +u8 k051649_scc_core::scc_r(bool is_sccplus, u8 address) +{ + const u8 voice = bitfield(address, 5, 3); + const u8 wave = bitfield(address, 0, 5); + u8 ret = 0xff; + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3 Waveform + case 0b101: // 0xa0-0xbf Voice 4 Waveform + ret = wave_r(false, (std::min(4, voice) << 5) | wave); + break; + } + return ret; +} + +u8 k052539_scc_core::scc_r(bool is_sccplus, u8 address) +{ + const u8 voice = bitfield(address, 5, 3); + const u8 wave = bitfield(address, 0, 5); + u8 ret = 0xff; + if (is_sccplus) + { + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3 Waveform + case 0b100: // 0x80-0x9f Voice 4 Waveform + ret = wave_r(true, address); + break; + } + } + else + { + switch (voice) + { + case 0b000: // 0x00-0x1f Voice 0 Waveform + case 0b001: // 0x20-0x3f Voice 1 Waveform + case 0b010: // 0x40-0x5f Voice 2 Waveform + case 0b011: // 0x60-0x7f Voice 3 Waveform + case 0b101: // 0xa0-0xbf Voice 4 Waveform + ret = wave_r(false, (std::min(4, voice) << 5) | wave); + break; + } + } + return ret; +} + +// Mapper +void k051649_core::reset() +{ + k051649_scc_core::reset(); + m_mapper.reset(); + m_scc_enable = false; +} + +void k052539_core::reset() +{ + k052539_scc_core::reset(); + m_mapper.reset(); + m_scc_enable = false; + m_is_sccplus = false; +} + +void k051649_core::k051649_mapper_t::reset() +{ + m_bank[0] = 0; + m_bank[1] = 1; + m_bank[2] = 2; + m_bank[3] = 3; +} + +void k052539_core::k052539_mapper_t::reset() +{ + m_bank[0] = 0; + m_bank[1] = 1; + m_bank[2] = 2; + m_bank[3] = 3; + std::fill(m_ram_enable.begin(), m_ram_enable.end(), false); +} + +// Mapper accessors +u8 k051649_core::read(u16 address) +{ + if ((bitfield(address, 11, 5) == 0b10011) && m_scc_enable) + { + return scc_r(false, u8(address)); + } + + return m_intf.read_byte((u32(m_mapper.bank(bitfield(address, 13, 2) ^ 2)) << 13) | + bitfield(address, 0, 13)); +} + +u8 k052539_core::read(u16 address) +{ + if ((bitfield(address, 11, 5) == 0b10011) && m_scc_enable && (!m_is_sccplus)) + { + return scc_r(false, u8(address)); + } + + if ((bitfield(address, 11, 5) == 0b10111) && m_scc_enable && m_is_sccplus) + { + return scc_r(true, u8(address)); + } + + return m_intf.read_byte((u32(m_mapper.bank(bitfield(address, 13, 2) ^ 2)) << 13) | + bitfield(address, 0, 13)); +} + +void k051649_core::write(u16 address, u8 data) +{ + const u16 bank = bitfield(address, 13, 2) ^ 2; + switch (bitfield(address, 11, 5)) + { + case 0b01010: // 0x5000-0x57ff Bank 0 + case 0b01110: // 0x7000-0x77ff Bank 1 + case 0b10010: // 0x9000-0x97ff Bank 2 + case 0b10110: // 0xb000-0xb7ff Bank 3 + m_mapper.set_bank(bank, data); + m_scc_enable = (bitfield(m_mapper.bank(2), 0, 6) == 0x3f); + break; + case 0b10011: // 0x9800-9fff SCC + if (m_scc_enable) + { + scc_w(false, u8(address), data); + } + break; + } +} + +void k052539_core::write(u16 address, u8 data) +{ + u8 prev = 0; + bool update = false; + const u16 bank = bitfield(address, 13, 2) ^ 2; + const bool ram_enable = m_mapper.ram_enable(bank); + if (ram_enable) + { + m_intf.write_byte((u32(m_mapper.bank(bank)) << 13) | bitfield(address, 0, 13), data); + } + switch (bitfield(address, 11, 5)) + { + case 0b01010: // 0x5000-0x57ff Bank 0 + case 0b01110: // 0x7000-0x77ff Bank 1 + case 0b10010: // 0x9000-0x97ff Bank 2 + case 0b10110: // 0xb000-0xb7ff Bank 3 + if (!ram_enable) + { + prev = m_mapper.bank(bank); + m_mapper.set_bank(bank, data); + update = prev ^ m_mapper.bank(bank); + } + break; + case 0b10011: // 0x9800-0x9fff SCC + if ((!ram_enable) && m_scc_enable && (!m_is_sccplus)) + { + scc_w(false, u8(address), data); + } + break; + case 0b10111: // 0xb800-0xbfff SCC+, Mapper configuration + if (bitfield(address, 1, 10) == 0x3ff) + { + m_mapper.set_ram_enable(0, bitfield(data, 4) || bitfield(data, 0)); + m_mapper.set_ram_enable(1, bitfield(data, 4) || bitfield(data, 1)); + m_mapper.set_ram_enable(2, bitfield(data, 4) || bitfield(data, 2)); + m_mapper.set_ram_enable(3, bitfield(data, 4)); + prev = (m_is_sccplus ? 1 : 0); + m_is_sccplus = bitfield(data, 5); + update = prev ^ (m_is_sccplus ? 1 : 0); + } + else if ((!ram_enable) && m_scc_enable && m_is_sccplus) + { + scc_w(true, u8(address), data); + } + break; + } + if (update) + { + m_scc_enable = + m_is_sccplus ? bitfield(m_mapper.bank(3), 7) : (bitfield(m_mapper.bank(2), 0, 6) == 0x3f); + } +} diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp b/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp new file mode 100644 index 000000000..8824d2535 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp @@ -0,0 +1,320 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst + Konami SCC emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_SCC_HPP +#define _VGSOUND_EMU_SRC_SCC_HPP + +#pragma once + +#include "../core/util.hpp" + +// shared for SCCs +class scc_core : public vgsound_emu_core +{ + private: + // classes + class voice_t : public vgsound_emu_core + { + public: + // constructor + voice_t(scc_core &host) + : vgsound_emu_core("scc_voice") + , m_host(host) + , m_wave{0} + , m_enable(false) + , m_pitch(0) + , m_volume(0) + , m_addr(0) + , m_counter(0) + , m_out(0) + { + } + + // internal state + void reset(); + void tick(); + + // accessors + inline void reset_addr() { m_addr = 0; } + + // setters + inline void set_wave(u8 addr, s8 wave) { m_wave[addr & 0x1f] = wave; } + + inline void set_enable(bool enable) { m_enable = enable; } + + inline void set_pitch(u16 pitch, u16 mask = 0xfff) + { + m_pitch = (m_pitch & ~(mask & 0xfff)) | (pitch & (mask & 0xfff)); + m_counter = m_pitch; + } + + inline void set_volume(u8 volume) { m_volume = volume & 0xf; } + + // getters + inline s8 wave(u8 addr) { return m_wave[addr & 0x1f]; } + + inline u8 addr() { return m_addr; } + + inline s32 out() { return m_out; } + + private: + // registers + scc_core &m_host; + std::array m_wave = {0}; // internal waveform + bool m_enable = false; // output enable flag + u16 m_pitch : 12; // pitch + u16 m_volume : 4; // volume + u8 m_addr = 0; // waveform pointer + u16 m_counter = 0; // frequency counter + s32 m_out = 0; // current output + }; + + class test_t : public vgsound_emu_core + { + public: + // constructor + test_t() + : vgsound_emu_core("scc_test") + , m_freq_4bit(0) + , m_freq_8bit(0) + , m_resetpos(0) + , m_rotate(0) + , m_rotate4(0) + { + } + + void reset() + { + m_freq_4bit = 0; + m_freq_8bit = 0; + m_resetpos = 0; + m_rotate = 0; + m_rotate4 = 0; + } + + // setters + inline void set_freq_4bit(bool freq_4bit) { m_freq_4bit = freq_4bit; } + + inline void set_freq_8bit(bool freq_8bit) { m_freq_8bit = freq_8bit; } + + inline void set_resetpos(bool resetpos) { m_resetpos = resetpos; } + + inline void set_rotate(bool rotate) { m_rotate = rotate; } + + inline void set_rotate4(bool rotate4) { m_rotate4 = rotate4; } + + // getters + inline bool freq_4bit() { return m_freq_4bit; } + + inline bool freq_8bit() { return m_freq_8bit; } + + inline bool resetpos() { return m_resetpos; } + + inline bool rotate() { return m_rotate; } + + inline bool rotate4() { return m_rotate4; } + + private: + u8 m_freq_4bit : 1; // 4 bit frequency + u8 m_freq_8bit : 1; // 8 bit frequency + u8 m_resetpos : 1; // reset counter after pitch writes + u8 m_rotate : 1; // rotate and write protect waveform for all channels + u8 m_rotate4 : 1; // same as above but for channel 4 only + }; + + public: + // constructor + scc_core(std::string tag) + : vgsound_emu_core(tag) + , m_voice{*this, *this, *this, *this, *this} + , m_test(test_t()) + , m_out(0) + , m_reg{0} + { + } + + // destructor + virtual ~scc_core() {} + + // accessors + virtual u8 scc_r(bool is_sccplus, u8 address) = 0; + virtual void scc_w(bool is_sccplus, u8 address, u8 data) = 0; + + // internal state + virtual void reset(); + void tick(); + + // getters + inline s32 out() { return m_out; } // output to DA0...DA10 pin + + inline u8 reg(u8 address) { return m_reg[address]; } + + // for preview + inline s32 voice_out(u8 voice) { return (voice < 5) ? m_voice[voice].out() : 0; } + + protected: + // accessor + u8 wave_r(bool is_sccplus, u8 address); + void wave_w(bool is_sccplus, u8 address, u8 data); + void freq_vol_enable_w(u8 address, u8 data); + + // internal values + std::array m_voice; // 5 voices + + test_t m_test; // test register + s32 m_out = 0; // output to DA0...10 + + std::array m_reg = {0}; // register pool +}; + +// SCC core +class k051649_scc_core : public scc_core +{ + public: + // constructor + k051649_scc_core(std::string tag = "k051649_scc") + : scc_core(tag) + { + } + + // accessors + virtual u8 scc_r(bool is_sccplus, u8 address) override; + virtual void scc_w(bool is_sccplus, u8 address, u8 data) override; +}; + +class k052539_scc_core : public k051649_scc_core +{ + public: + // constructor + k052539_scc_core(std::string tag = "k052539_scc") + : k051649_scc_core(tag) + { + } + + // accessors + virtual u8 scc_r(bool is_sccplus, u8 address) override; + virtual void scc_w(bool is_sccplus, u8 address, u8 data) override; +}; + +// MegaROM Mapper with SCC +class k051649_core : public k051649_scc_core +{ + friend class vgsound_emu_mem_intf; // for megaROM mapper + + private: + // mapper classes + class k051649_mapper_t : public vgsound_emu_core + { + public: + k051649_mapper_t() + : vgsound_emu_core("k051649_mapper") + , m_bank{0, 1, 2, 3} + { + } + + // internal state + void reset(); + + // setters + inline void set_bank(u8 slot, u8 bank) { m_bank[slot & 3] = bank; } + + // getters + inline u8 bank(u8 slot) { return m_bank[slot & 3]; } + + private: + // registers + u8 m_bank[4] = {0, 1, 2, 3}; + }; + + public: + // constructor + k051649_core(vgsound_emu_mem_intf &intf) + : k051649_scc_core("k051649") + , m_intf(intf) + , m_mapper(k051649_mapper_t()) + , m_scc_enable(false) + { + } + + // accessors + u8 read(u16 address); + void write(u16 address, u8 data); + + virtual void reset() override; + + private: + vgsound_emu_mem_intf m_intf; + k051649_mapper_t m_mapper; + bool m_scc_enable = false; +}; + +// MegaRAM Mapper with SCC +class k052539_core : public k052539_scc_core +{ + friend class vgsound_emu_mem_intf; // for megaRAM mapper + + private: + // mapper classes + class k052539_mapper_t : public vgsound_emu_core + { + public: + k052539_mapper_t() + : vgsound_emu_core("k052539_mapper") + , m_bank{0, 1, 2, 3} + , m_ram_enable{false} + { + } + + // internal state + void reset(); + + // setters + inline void set_bank(u8 slot, u8 bank) { m_bank[slot & 3] = bank; } + + inline void set_ram_enable(u8 slot, bool ram_enable) + { + m_ram_enable[slot & 3] = ram_enable; + } + + // getters + inline u8 bank(u8 slot) { return m_bank[slot & 3]; } + + inline bool ram_enable(u8 slot) { return m_ram_enable[slot & 3]; } + + private: + // registers + std::array m_bank = {0, 1, 2, 3}; + std::array m_ram_enable = {false}; + }; + + public: + // constructor + k052539_core(vgsound_emu_mem_intf &intf) + : k052539_scc_core("k052539") + , m_intf(intf) + , m_mapper(k052539_mapper_t()) + , m_scc_enable(false) + , m_is_sccplus(false) + { + } + + // accessors + u8 read(u16 address); + void write(u16 address, u8 data); + + virtual void reset() override; + + private: + vgsound_emu_mem_intf m_intf; + k052539_mapper_t m_mapper; + bool m_scc_enable = false; + bool m_is_sccplus = false; +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/template/template.cpp b/vgsound_emu-modified/vgsound_emu/src/template/template.cpp new file mode 100644 index 000000000..d927860a0 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/template/template.cpp @@ -0,0 +1,33 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): (Author name) + Template for sound emulation core +*/ + +#include "template.hpp" + +void template_core::tick() +{ + // tick per each clock +} + +void template_core::reset() +{ + // reset this chip + std::fill(m_array.begin(), m_array.end(), 0); // std::fill() for fill std::array, std::vector +} + +/* +template voice function +void template_core::voice_t::tick() +{ + // tick per each voice +} + +void template_core::voice_t::reset() +{ + // reset this voice +} +*/ diff --git a/vgsound_emu-modified/vgsound_emu/src/template/template.hpp b/vgsound_emu-modified/vgsound_emu/src/template/template.hpp new file mode 100644 index 000000000..ca8f91b53 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/template/template.hpp @@ -0,0 +1,88 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): (Author name) + Template for sound emulation core, also guideline +*/ + +#ifndef _VGSOUND_EMU_SRC_TEMPLATE_HPP // _VGSOUND_EMU_ABSOLUTE_PATH_OF_THIS_FILE +#define _VGSOUND_EMU_SRC_TEMPLATE_HPP + +#pragma once + +#include "../core/util.hpp" + +class template_core : public vgsound_emu_core +{ + friend class vgsound_emu_mem_intf; // common memory interface if exists + + private: // protected: if shares between inheritances + // place classes and local constants here if exists + + // template voice classes + class voice_t : public vgsound_emu_core + { + public: + // constructor + voice_t(template_core &host) + : vgsound_emu_core("your_voice_tag_here") + , m_host(host) + , m_something(0) + { + } + + // internal state + void reset(); + void tick(); + + // accessors, getters, setters + + // setters + void set_something(s32 something) { m_something = something; } + + // getters + s32 something() { return m_something; } + + private: + // registers + template_core &m_host; + s32 m_something = 0; // register + }; + + public: + // place constructor and destructor, getter and setter for local variables, + // off-chip interfaces, update routine here only if can't be local + + // constructor + template_core(vgsound_emu_mem_intf &intf) + : vgsound_emu_core("your_core_tag_here") + // initialize all variables in constructor, because constructor is also executable + // anywhere, and it works as initializer. + , m_voice{*this} + , m_intf(intf) + , m_array{0} + , m_vector{0} + { + } + + // accessors, getters, setters + + // internal state + void reset(); + void tick(); + + protected: + // place local variables and functions here if shares between inheritances + + private: + // place local variables and functions here + + std::array m_voice; // voice classes + vgsound_emu_mem_intf &m_intf; // common memory interface + std::array m_array = { + 0}; // std::array for static size array + std::vector m_vector = {0}; // std::vector for variable size array +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md b/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md new file mode 100644 index 000000000..5b2db0519 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md @@ -0,0 +1,97 @@ + +# Konami VRC VI + +## Summary + +- 2 voice pulse wave + - 8 level duty or volume only mode +- 1 voice sawtooth wave +- Internal mapper and timer + +## Source code + +- vrcvi.hpp: Base header + - vrcvi.cpp: Source emulation core + +## Description + +It's one of NES mapper with built-in sound chip, and also one of 2 Konami VRCs with this feature. (rest one has OPLL derivatives.) + +It's also DACless like other sound chip and mapper-with-sound manufactured by konami, the Chips 6 bit digital sound output is needs converted to analog sound output when you it want to make some sounds, or send to sound mixer. + +Its are used for Akumajou Densetsu (Japan release of Castlevania III), Madara, Esper Dream 2. + +The chip is installed in 351951 PCB and 351949A PCB. + +351951 PCB is used exclusivly for Akumajou Densetsu, Small board has VRC VI, PRG and CHR ROM. + +- It's configuration also calls VRC6a, iNES mapper 024. + +351949A PCB is for Last 2 titles with VRC VI, Bigger board has VRC VI, PRG and CHR ROM, and Battery Backed 8K x 8 bit SRAM. + +- Additionally, It's PRG A0 and A1 bit to VRC VI input is swapped, compare to above. +- It's configuration also calls VRC6b, iNES mapper 026. + +The chip itself has 053328, 053329, 053330 Revision, but Its difference between revision is unknown. + +Like other mappers for NES, It has internal timer - Its timer can be sync with scanline like other Konami mapper in this era. + +## Register layout + +- Sound and Timer only; 351951 PCB case, 351949A swaps xxx1 and xxx2 + +``` +Address Bits Description + 7654 3210 + +9000-9002 Pulse 1 + +9000 x--- ---- Pulse 1 Duty ignore + -xxx ---- Pulse 1 Duty cycle + ---- xxxx Pulse 1 Volume +9001 xxxx xxxx Pulse 1 Pitch bit 0-7 +9002 x--- ---- Pulse 1 Enable + ---- xxxx Pulse 1 Pitch bit 8-11 + +9003 Sound control + +9003 ---- -x-- 4 bit Frequency mode + ---- -0x- 8 bit Frequency mode + ---- ---x Halt + +a000-a002 Pulse 2 + +a000 x--- ---- Pulse 2 Duty ignore + -xxx ---- Pulse 2 Duty cycle + ---- xxxx Pulse 2 Volume +a001 xxxx xxxx Pulse 2 Pitch bit 0-7 +a002 x--- ---- Pulse 2 Enable + ---- xxxx Pulse 2 Pitch bit 8-11 + +b000-b002 Sawtooth + +b000 --xx xxxx Sawtooth Accumulate Rate +b001 xxxx xxxx Sawtooth Pitch bit 0-7 +b002 x--- ---- Sawtooth Enable + ---- xxxx Sawtooth Pitch bit 8-11 + +f000-f002 IRQ Timer + +f000 xxxx xxxx IRQ Timer latch +f001 ---- -0-- Sync with scanline + ---- --x- Enable timer + ---- ---x Enable timer after IRQ Acknowledge +f002 ---- ---- IRQ Acknowledge +``` + +# Frequency calculation + +``` + if 4 bit Frequency Mode then + Frequency: Input clock / (bit 8 to 11 of Pitch + 1) + end else if 8 bit Frequency Mode then + Frequency: Input clock / (bit 4 to 11 of Pitch + 1) + end else then + Frequency: Input clock / (Pitch + 1) + end +``` diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp b/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp new file mode 100644 index 000000000..dc59120f3 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp @@ -0,0 +1,260 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami VRC VI sound emulation core +*/ + +#include "vrcvi.hpp" + +void vrcvi_core::tick() +{ + m_out = 0; + if (!m_control.halt()) // Halt flag + { + // tick per each clock + for (auto &elem : m_pulse) + { + m_out += elem.get_output(); // add 4 bit pulse output + } + m_out += m_sawtooth.get_output(); // add 5 bit sawtooth output + } + if (m_timer.tick()) + { + m_timer.counter_tick(); + } +} + +void vrcvi_core::reset() +{ + for (auto &elem : m_pulse) + { + elem.reset(); + } + + m_sawtooth.reset(); + m_timer.reset(); + m_control.reset(); + m_out = 0; +} + +bool vrcvi_core::alu_t::tick() +{ + if (m_divider.enable()) + { + const u16 temp = m_counter; + // post decrement + if (bitfield(m_host.m_control.shift(), 1)) + { + m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); + m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); + } + else if (bitfield(m_host.m_control.shift(), 0)) + { + m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4); + m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0); + } + else + { + m_counter = bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12); + } + + // carry handling + bool carry = bitfield(m_host.m_control.shift(), 1) + ? (bitfield(temp, 8, 4) == 0) + : (bitfield(m_host.m_control.shift(), 0) ? (bitfield(temp, 4, 8) == 0) + : (bitfield(temp, 0, 12) == 0)); + if (carry) + { + m_counter = m_divider.divider(); + } + + return carry; + } + return false; +} + +bool vrcvi_core::pulse_t::tick() +{ + if (!m_divider.enable()) + { + return false; + } + + if (vrcvi_core::alu_t::tick()) + { + m_cycle = bitfield(m_cycle + 1, 0, 4); + } + + return m_control.mode() ? true : ((m_cycle > m_control.duty()) ? true : false); +} + +bool vrcvi_core::sawtooth_t::tick() +{ + if (!m_divider.enable()) + { + return false; + } + + if (vrcvi_core::alu_t::tick()) + { + if (bitfield(m_cycle++, 0)) + { // Even step only + m_accum += m_rate; + } + if (m_cycle >= 14) // Reset accumulator at every 14 cycles + { + m_accum = 0; + m_cycle = 0; + } + } + return (m_accum == 0) ? false : true; +} + +s8 vrcvi_core::pulse_t::get_output() +{ + // add 4 bit pulse output + m_out = tick() ? m_control.volume() : 0; + return m_out; +} + +s8 vrcvi_core::sawtooth_t::get_output() +{ + // add 5 bit sawtooth output + m_out = tick() ? bitfield(m_accum, 3, 5) : 0; + return m_out; +} + +void vrcvi_core::alu_t::reset() +{ + m_divider.reset(); + m_counter = 0; + m_cycle = 0; + m_out = 0; +} + +void vrcvi_core::pulse_t::reset() +{ + vrcvi_core::alu_t::reset(); + m_control.reset(); +} + +void vrcvi_core::sawtooth_t::reset() +{ + vrcvi_core::alu_t::reset(); + m_rate = 0; + m_accum = 0; +} + +bool vrcvi_core::timer_t::tick() +{ + if (m_timer_control.enable()) + { + if (!m_timer_control.sync()) // scanline sync mode + { + m_prescaler -= 3; + if (m_prescaler <= 0) + { + m_prescaler += 341; + return true; + } + } + } + return (m_timer_control.enable() && m_timer_control.sync()) ? true : false; +} + +void vrcvi_core::timer_t::counter_tick() +{ + if (bitfield(++m_counter, 0, 8) == 0) + { + m_counter = m_counter_latch; + irq_set(); + } +} + +void vrcvi_core::timer_t::reset() +{ + m_timer_control.reset(); + m_prescaler = 341; + m_counter = m_counter_latch = 0; + irq_clear(); +} + +// Accessors + +void vrcvi_core::alu_t::divider_t::write(bool msb, u8 data) +{ + if (msb) + { + m_divider = (m_divider & ~0xf00) | (bitfield(data, 0, 4) << 8); + m_enable = bitfield(data, 7); + } + else + { + m_divider = (m_divider & ~0x0ff) | data; + } +} + +void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data) +{ + pulse_t &v = m_pulse[voice]; + switch (address) + { + case 0x00: // Control - 0x9000 (Pulse 1), 0xa000 (Pulse 2) + v.control().write(data); + break; + case 0x01: // Pitch LSB - 0x9001/0x9002 (Pulse 1), 0xa001/0xa002 (Pulse 2) + v.divider().write(false, data); + break; + case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2) + v.divider().write(true, data); + if (!v.divider().enable()) + { // Reset duty cycle + v.clear_cycle(); + } + break; + } +} + +void vrcvi_core::saw_w(u8 address, u8 data) +{ + switch (address) + { + case 0x00: // Sawtooth Accumulate - 0xb000 + m_sawtooth.set_rate(bitfield(data, 0, 6)); + break; + case 0x01: // Pitch LSB - 0xb001/0xb002 (Sawtooth) + m_sawtooth.divider().write(false, data); + break; + case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth) + m_sawtooth.divider().write(true, data); + if (!m_sawtooth.divider().enable()) + { // Reset accumulator + m_sawtooth.clear_accum(); + } + break; + } +} + +void vrcvi_core::timer_w(u8 address, u8 data) +{ + switch (address) + { + case 0x00: // Timer latch - 0xf000 + m_timer.set_counter_latch(data); + break; + case 0x01: // Timer control - 0xf001/0xf002 + m_timer.timer_control_w(data); + break; + case 0x02: // IRQ Acknowledge - 0xf002/0xf001 + m_timer.irq_ack(); + break; + } +} + +void vrcvi_core::control_w(u8 data) +{ + // Global control - 0x9003 + m_control.write(data); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp b/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp new file mode 100644 index 000000000..f163114c0 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp @@ -0,0 +1,407 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Konami VRC VI sound emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_VRCVI_HPP +#define _VGSOUND_EMU_SRC_VRCVI_HPP + +#pragma once + +#include "../core/util.hpp" + +class vrcvi_intf : public vgsound_emu_core +{ + public: + vrcvi_intf() + : vgsound_emu_core("vrc_vi_intf") + { + } + + virtual void irq_w(bool irq) {} +}; + +class vrcvi_core : public vgsound_emu_core +{ + friend class vrcvi_intf; + + private: + // Common ALU for sound channels + class alu_t : public vgsound_emu_core + { + private: + class divider_t : public vgsound_emu_core + { + public: + divider_t() + : vgsound_emu_core("vrc_vi_frequency_divider") + , m_divider(0) + , m_enable(0) + { + } + + void reset() + { + m_divider = 0; + m_enable = 0; + } + + void write(bool msb, u8 data); + + // getters + inline u16 divider() { return m_divider; } + + inline bool enable() { return m_enable; } + + private: + u16 m_divider : 12; // divider (pitch) + u16 m_enable : 1; // channel disable flag + }; + + public: + alu_t(std::string tag, vrcvi_core &host) + : vgsound_emu_core(tag) + , m_host(host) + , m_divider(divider_t()) + , m_counter(0) + , m_cycle(0) + , m_out(0) + { + } + + virtual void reset(); + virtual bool tick(); + + virtual s8 get_output() + { + m_out = 0; + return 0; + } + + // accessors + inline void clear_cycle() { m_cycle = 0; } + + // getters + divider_t ÷r() { return m_divider; } + + inline u16 counter() { return m_counter; } + + inline u8 cycle() { return m_cycle; } + + // for previwe/debug only + inline s8 out() { return m_out; } + + protected: + vrcvi_core &m_host; + divider_t m_divider; + u16 m_counter = 0; // clock counter + u8 m_cycle = 0; // clock cycle + s8 m_out = 0; // output per channel + }; + + // 2 Pulse channels + class pulse_t : public alu_t + { + private: + // Control bits + class pulse_control_t + { + public: + pulse_control_t() + : m_mode(0) + , m_duty(0) + , m_volume(0) + { + } + + void reset() + { + m_mode = 0; + m_duty = 0; + m_volume = 0; + } + + // accessors + inline void write(u8 data) + { + m_mode = (data >> 7) & 0x1; + m_duty = (data >> 4) & 0x7; + m_volume = (data >> 0) & 0xf; + } + + // getters + inline bool mode() { return m_mode; } + + inline u8 duty() { return m_duty; } + + inline u8 volume() { return m_volume; } + + private: + u8 m_mode : 1; // duty toggle flag + u8 m_duty : 3; // 3 bit duty cycle + u8 m_volume : 4; // 4 bit volume + }; + + public: + pulse_t(vrcvi_core &host) + : alu_t("vrc_vi_pulse", host) + , m_control(pulse_control_t()) + { + } + + virtual void reset() override; + virtual bool tick() override; + virtual s8 get_output() override; + + // getters + pulse_control_t &control() { return m_control; } + + private: + pulse_control_t m_control; + }; + + // 1 Sawtooth channel + class sawtooth_t : public alu_t + { + public: + sawtooth_t(vrcvi_core &host) + : alu_t("vrc_vi_sawtooth", host) + , m_rate(0) + , m_accum(0) + { + } + + virtual void reset() override; + virtual bool tick() override; + virtual s8 get_output() override; + + // accessors + inline void clear_accum() { m_accum = 0; } + + // setters + inline void set_rate(u8 rate) { m_rate = rate; } + + // getters + inline u8 rate() { return m_rate; } + + inline u8 accum() { return m_accum; } + + private: + u8 m_rate = 0; // sawtooth accumulate rate + u8 m_accum = 0; // sawtooth accumulator, high 5 bit is accumulated to output + }; + + // Internal timer + class timer_t : public vgsound_emu_core + { + private: + // Control bits + class timer_control_t : public vgsound_emu_core + { + public: + timer_control_t() + : vgsound_emu_core("vrc_vi_timer_control") + , m_irq_trigger(0) + , m_enable_ack(0) + , m_enable(0) + , m_sync(0) + { + } + + void reset() + { + m_irq_trigger = 0; + m_enable_ack = 0; + m_enable = 0; + m_sync = 0; + } + + // accessors + inline void irq_set(bool irq) { m_irq_trigger = irq ? 1 : 0; } + + // setters + inline void set_enable_ack(bool enable_ack) + { + m_enable_ack = enable_ack ? 1 : 0; + } + + inline void set_enable(bool enable) { m_enable = enable ? 1 : 0; } + + inline void set_sync(bool sync) { m_sync = sync ? 1 : 0; } + + // getters + inline bool irq_trigger() { return m_irq_trigger; } + + inline bool enable_ack() { return m_enable_ack; } + + inline bool enable() { return m_enable; } + + inline bool sync() { return m_sync; } + + private: + u8 m_irq_trigger : 1; + u8 m_enable_ack : 1; + u8 m_enable : 1; + u8 m_sync : 1; + }; + + public: + timer_t(vrcvi_core &host) + : vgsound_emu_core("vrc_vi_timer") + , m_host(host) + , m_timer_control(timer_control_t()) + , m_prescaler(341) + , m_counter(0) + , m_counter_latch(0) + { + } + + void reset(); + bool tick(); + void counter_tick(); + + // IRQ update + void update() { m_host.m_intf.irq_w(m_timer_control.irq_trigger()); } + + void irq_set() + { + if (!m_timer_control.irq_trigger()) + { + m_timer_control.irq_set(true); + update(); + } + } + + void irq_clear() + { + if (m_timer_control.irq_trigger()) + { + m_timer_control.irq_set(false); + update(); + } + } + + // accessors + void reset_counter() + { + m_counter = m_counter_latch; + m_prescaler = 341; + } + + void timer_control_w(u8 data) + { + m_timer_control.set_enable_ack((data >> 0) & 1); + m_timer_control.set_enable((data >> 1) & 1); + m_timer_control.set_sync((data >> 2) & 1); + if (m_timer_control.enable()) + { + reset_counter(); + } + irq_clear(); + } + + void irq_ack() + { + irq_clear(); + m_timer_control.set_enable(m_timer_control.enable_ack()); + } + + // setters + inline void set_counter_latch(u8 counter_latch) { m_counter_latch = counter_latch; } + + // getters + timer_control_t &timer_control() { return m_timer_control; } + + inline s16 prescaler() { return m_prescaler; } + + inline u8 counter() { return m_counter; } + + inline u8 counter_latch() { return m_counter_latch; } + + private: + vrcvi_core &m_host; // host core + timer_control_t m_timer_control; // timer control bits + s16 m_prescaler = 341; // prescaler + u8 m_counter = 0; // clock counter + u8 m_counter_latch = 0; // clock counter latch + }; + + class global_control_t : public vgsound_emu_core + { + public: + global_control_t() + : vgsound_emu_core("vrc_vi_global_control") + , m_halt(0) + , m_shift(0) + { + } + + void reset() + { + m_halt = 0; + m_shift = 0; + } + + // accessors + inline void write(u8 data) + { + m_halt = (data >> 0) & 1; + m_shift = (data >> 1) & 3; + } + + // getters + inline bool halt() { return m_halt; } + + inline u8 shift() { return m_shift; } + + private: + u8 m_halt : 1; // halt sound + u8 m_shift : 2; // 4/8 bit right shift + }; + + public: + // constructor + vrcvi_core(vrcvi_intf &intf) + : vgsound_emu_core("vrc_vi") + , m_intf(intf) + , m_pulse{*this, *this} + , m_sawtooth(*this) + , m_timer(*this) + , m_control(global_control_t()) + , m_out(0) + { + } + + // accessors, getters, setters + void pulse_w(u8 voice, u8 address, u8 data); + void saw_w(u8 address, u8 data); + void timer_w(u8 address, u8 data); + void control_w(u8 data); + + // internal state + void reset(); + void tick(); + + // 6 bit output + inline s8 out() { return m_out; } + + // for debug/preview only + inline s8 pulse_out(u8 pulse) { return (pulse < 2) ? m_pulse[pulse].out() : 0; } + + inline s8 sawtooth_out() { return m_sawtooth.out(); } + + private: + vrcvi_intf &m_intf; + + std::array m_pulse; // 2 pulse channels + sawtooth_t m_sawtooth; // sawtooth channel + timer_t m_timer; // internal timer + global_control_t m_control; // control + + s8 m_out = 0; // 6 bit output +}; + +#endif diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md b/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md new file mode 100644 index 000000000..656e5c60f --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md @@ -0,0 +1,97 @@ + +# Seta/Allumer X1-010 + +## Summary + +- 16 voice wavetable or PCM + - 8 bit signed for both wavetable and PCM + - 128 width long waveform + - wavetable playback must be paired with envelope + - envelope shape is 4 bit stereo, 128 width long waveform + - waveform and envelope shape stored at each half area on RAM space + - total accessible memory for PCM: 1 MByte + +## Source code + +- x1_010.hpp: Base header + - x1_010.cpp: Source emulation core + +## Description + +the chip has 16 voices, all voices can be switchable to Wavetable or PCM sample playback mode. It has also 2 output channels, but no known hardware using this feature for stereo sound. + +Wavetable needs to paired with envelope, it's always enabled and similar as AY PSG's one but its shape is stored at RAM. + +PCM volume is stored by each register. + +Both volume is 4bit per output. + +Everything except PCM sample is stored at paired 8 bit RAM. + +## RAM layout + +common case: Address bit 12 is swapped when RAM is shared with CPU + +### Voice registers (0000...007f) + +0000...0007 Voice #0 Register + +``` +Address Bits Description + 7654 3210 +0 x--- ---- Frequency divider* + ---- -x-- Envelope one-shot mode + ---- --x- Sound format + ---- --0- PCM + ---- --1- Wavetable + ---- ---x Keyon/off +PCM case: +1 xxxx xxxx Volume (Each nibble is for each output) + +2 xxxx xxxx Frequency + +4 xxxx xxxx Start address / 4096 + +5 xxxx xxxx 0x100 - (End address / 4096) +Wavetable case: +1 ---x xxxx Wavetable data select + +2 xxxx xxxx Frequency LSB +3 xxxx xxxx "" MSB + +4 xxxx xxxx Envelope period + +5 ---x xxxx Envelope shape select (!= 0 : Reserved for Voice registers) +``` + +0008...000f Voice #1 Register +... +0078...007f Voice #15 Register + +### Envelope shape data (0080...0fff) + +Same format as volume; Each nibble is for each output + +0080...00ff Envelope shape #1 data +0100...017f Envelope shape #2 data +... +0f80...0fff Envelope shape #31 data + +### Waveform data (1000...1fff) + +1000...107f Waveform #0 data +1080...10ff Waveform #1 data +... +1f80...1fff Waveform #31 data + +## Frequency calculation + +``` +Wavetable, Divider Clear: Frequency value * (Input clock / 524288) +Wavetable, Divider Set: Frequency value * (Input clock / 1048576) +PCM, Divider Clear: Frequency value * (Input clock / 8192) +PCM, Divider Set: Frequency value * (Input clock / 16384) +Envelope: Envelope period * (Input clock / 524288) - Frequency divider not affected? +``` + +Frequency divider is higher precision or just right shift? needs verification. diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp b/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp new file mode 100644 index 000000000..c249e5548 --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp @@ -0,0 +1,163 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Seta/Allumer X1-010 Emulation core +*/ + +#include "x1_010.hpp" + +void x1_010_core::tick() +{ + // reset output + m_out[0] = m_out[1] = 0; + for (voice_t &elem : m_voice) + { + elem.tick(); + m_out[0] += elem.out(0); + m_out[1] += elem.out(1); + } +} + +void x1_010_core::voice_t::tick() +{ + m_out[0] = m_out[1] = 0; + if (m_flag.keyon()) + { + if (m_flag.wavetable()) // Wavetable + { + // envelope, each nibble is for each output + u8 vol = + m_host.m_envelope[(bitfield(m_end_envshape, 0, 5) << 7) | bitfield(m_env_acc, 10, 7)]; + m_vol_out[0] = bitfield(vol, 4, 4); + m_vol_out[1] = bitfield(vol, 0, 4); + m_env_acc += m_start_envfreq; + if (m_flag.env_oneshot() && bitfield(m_env_acc, 17)) + { + m_flag.set_keyon(false); + } + else + { + m_env_acc = bitfield(m_env_acc, 0, 17); + } + // get wavetable data + m_data = m_host.m_wave[(bitfield(m_vol_wave, 0, 5) << 7) | bitfield(m_acc, 11, 7)]; + m_acc = bitfield(m_acc + (m_freq << (1 - m_flag.div())), 0, 18); + } + else // PCM sample + { + // volume register, each nibble is for each output + m_vol_out[0] = bitfield(m_vol_wave, 4, 4); + m_vol_out[1] = bitfield(m_vol_wave, 0, 4); + // get PCM sample + m_data = m_host.m_intf.read_byte(bitfield(m_acc, 5, 20)); + m_acc += u32(bitfield(m_freq, 0, 8)) << (1 - m_flag.div()); + if ((m_acc >> 17) > u32(0xff ^ m_end_envshape)) + { + m_flag.set_keyon(false); + } + } + m_out[0] = m_data * m_vol_out[0]; + m_out[1] = m_data * m_vol_out[1]; + } +} + +u8 x1_010_core::ram_r(u16 offset) +{ + if (offset & 0x1000) + { // wavetable data + return m_wave[offset & 0xfff]; + } + else if (offset & 0xf80) + { // envelope shape data + return m_envelope[offset & 0xfff]; + } + else + { // channel register + return m_voice[bitfield(offset, 3, 4)].reg_r(offset & 0x7); + } +} + +void x1_010_core::ram_w(u16 offset, u8 data) +{ + if (offset & 0x1000) + { // wavetable data + m_wave[offset & 0xfff] = data; + } + else if (offset & 0xf80) + { // envelope shape data + m_envelope[offset & 0xfff] = data; + } + else + { // channel register + m_voice[bitfield(offset, 3, 4)].reg_w(offset & 0x7, data); + } +} + +u8 x1_010_core::voice_t::reg_r(u8 offset) +{ + switch (offset & 0x7) + { + case 0x00: + return (m_flag.div() << 7) | (m_flag.env_oneshot() << 2) | (m_flag.wavetable() << 1) | + (m_flag.keyon() << 0); + case 0x01: return m_vol_wave; + case 0x02: return bitfield(m_freq, 0, 8); + case 0x03: return bitfield(m_freq, 8, 8); + case 0x04: return m_start_envfreq; + case 0x05: return m_end_envshape; + default: break; + } + return 0; +} + +void x1_010_core::voice_t::reg_w(u8 offset, u8 data) +{ + switch (offset & 0x7) + { + case 0x00: + { + const bool prev_keyon = m_flag.keyon(); + m_flag.write(data); + if (!prev_keyon && m_flag.keyon()) // Key on + { + m_acc = m_flag.wavetable() ? 0 : (u32(m_start_envfreq) << 16); + m_env_acc = 0; + } + break; + } + case 0x01: m_vol_wave = data; break; + case 0x02: m_freq = (m_freq & 0xff00) | data; break; + case 0x03: m_freq = (m_freq & 0x00ff) | (u16(data) << 8); break; + case 0x04: m_start_envfreq = data; break; + case 0x05: m_end_envshape = data; break; + default: break; + } +} + +void x1_010_core::voice_t::reset() +{ + m_flag.reset(); + m_vol_wave = 0; + m_freq = 0; + m_start_envfreq = 0; + m_end_envshape = 0; + m_acc = 0; + m_env_acc = 0; + m_data = 0; + m_vol_out.fill(0); + m_out.fill(0); +} + +void x1_010_core::reset() +{ + for (auto &elem : m_voice) + { + elem.reset(); + } + + m_envelope.fill(0); + m_wave.fill(0); + m_out.fill(0); +} diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp b/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp new file mode 100644 index 000000000..34a4ab99a --- /dev/null +++ b/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp @@ -0,0 +1,179 @@ +/* + License: Zlib + see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details + + Copyright holder(s): cam900 + Seta/Allumer X1-010 Emulation core +*/ + +#ifndef _VGSOUND_EMU_SRC_X1_010_HPP +#define _VGSOUND_EMU_SRC_X1_010_HPP + +#pragma once + +#include "../core/util.hpp" + +class x1_010_core : public vgsound_emu_core +{ + friend class vgsound_emu_mem_intf; + + private: + // 16 voices in chip + class voice_t : public vgsound_emu_core + { + private: + class flag_t : public vgsound_emu_core + { + public: + flag_t() + : vgsound_emu_core("x1_010_voice_flag") + , m_div(0) + , m_env_oneshot(0) + , m_wavetable(0) + , m_keyon(0) + { + } + + // internal state + void reset() + { + m_div = 0; + m_env_oneshot = 0; + m_wavetable = 0; + m_keyon = 0; + } + + // register accessor + inline void write(u8 data) + { + m_div = (data >> 7) & 1; + m_env_oneshot = (data >> 2) & 1; + m_wavetable = (data >> 1) & 1; + m_keyon = (data >> 0) & 1; + } + + // Setters + inline void set_keyon(bool keyon) { m_keyon = keyon; } + + // Getters + inline bool div() { return m_div; } + + inline bool env_oneshot() { return m_env_oneshot; } + + inline bool wavetable() { return m_wavetable; } + + inline bool keyon() { return m_keyon; } + + private: + u8 m_div : 1; + u8 m_env_oneshot : 1; + u8 m_wavetable : 1; + u8 m_keyon : 1; + }; + + public: + // constructor + voice_t(x1_010_core &host) + : vgsound_emu_core("x1_010_voice") + , m_host(host) + , m_flag(flag_t()) + , m_vol_wave(0) + , m_freq(0) + , m_start_envfreq(0) + , m_end_envshape(0) + , m_acc(0) + , m_env_acc(0) + , m_data(0) + , m_vol_out{0} + , m_out{0} + { + } + + // internal state + void reset(); + void tick(); + + // register accessor + u8 reg_r(u8 offset); + void reg_w(u8 offset, u8 data); + + // getters + inline s32 out(u8 ch) { return m_out[ch & 1]; } + + private: + // host flag + x1_010_core &m_host; + // registers + flag_t m_flag; + u8 m_vol_wave = 0; + u16 m_freq = 0; + u8 m_start_envfreq = 0; + u8 m_end_envshape = 0; + + // internal registers + u32 m_acc = 0; + u32 m_env_acc = 0; + s8 m_data = 0; + std::array m_vol_out = {0}; + + // for preview only + std::array m_out = {0}; + }; + + public: + // constructor + x1_010_core(vgsound_emu_mem_intf &intf) + : vgsound_emu_core("x1_010") + , m_voice{*this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this, + *this} + , m_intf(intf) + , m_envelope{0} + , m_wave{0} + , m_out{0} + { + } + + // register accessor + u8 ram_r(u16 offset); + void ram_w(u16 offset, u8 data); + + // getters + inline s32 output(u8 ch) { return m_out[ch & 1]; } + + // internal state + void reset(); + void tick(); + + // for preview only + inline s32 voice_out(u8 voice, u8 ch) + { + return (voice < 16) ? m_voice[voice].out(ch & 1) : 0; + } + + private: + std::array m_voice; + vgsound_emu_mem_intf &m_intf; + + // RAM + std::array m_envelope = {0}; + std::array m_wave = {0}; + + // output data + std::array m_out = {0}; +}; + +#endif From 2582ec17ed082cb7ef1ff5655ab251fe93739565 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 00:15:04 +0900 Subject: [PATCH 12/63] Temporary workaround for accidently removed libs --- extern/vgsound_emu-modified | 1 - 1 file changed, 1 deletion(-) delete mode 160000 extern/vgsound_emu-modified diff --git a/extern/vgsound_emu-modified b/extern/vgsound_emu-modified deleted file mode 160000 index 7b988a671..000000000 --- a/extern/vgsound_emu-modified +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7b988a6714ebf61e8a5fad5c9ccbda2b85853fe1 From 5034b3623d8721722a3bb4832a2fc7205586d29d Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 00:16:20 +0900 Subject: [PATCH 13/63] Take 2 --- .../vgsound_emu-modified}/.clang-format | 0 {vgsound_emu-modified => extern/vgsound_emu-modified}/.gitignore | 0 .../vgsound_emu-modified}/CHANGELOG.md | 0 .../vgsound_emu-modified}/CMakeLists.txt | 0 {vgsound_emu-modified => extern/vgsound_emu-modified}/LICENSE | 0 {vgsound_emu-modified => extern/vgsound_emu-modified}/MODIFIED.md | 0 {vgsound_emu-modified => extern/vgsound_emu-modified}/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/core/util.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/core/vox/vox.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/core/vox/vox.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5504.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5504.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5505.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5505.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5506.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es5506.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es550x.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es550x.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/es550x/es550x_alu.cpp | 0 .../vgsound_emu/src/es550x/es550x_filter.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k005289/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/k005289/k005289.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k005289/k005289.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k007232/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/k007232/k007232.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k007232/k007232.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k053260/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/k053260/k053260.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/k053260/k053260.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/msm6295/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/msm6295/msm6295.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/msm6295/msm6295.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/n163/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/n163/n163.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/n163/n163.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/scc/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/scc/scc.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/scc/scc.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/template/template.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/template/template.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/vrcvi/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/vrcvi/vrcvi.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/vrcvi/vrcvi.hpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/x1_010/README.md | 0 .../vgsound_emu-modified}/vgsound_emu/src/x1_010/x1_010.cpp | 0 .../vgsound_emu-modified}/vgsound_emu/src/x1_010/x1_010.hpp | 0 47 files changed, 0 insertions(+), 0 deletions(-) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/.clang-format (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/.gitignore (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/CHANGELOG.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/CMakeLists.txt (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/LICENSE (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/MODIFIED.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/core/util.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/core/vox/vox.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/core/vox/vox.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5504.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5504.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5505.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5505.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5506.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es5506.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es550x.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es550x.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es550x_alu.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/es550x/es550x_filter.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k005289/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k005289/k005289.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k005289/k005289.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k007232/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k007232/k007232.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k007232/k007232.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k053260/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k053260/k053260.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/k053260/k053260.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/msm6295/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/msm6295/msm6295.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/msm6295/msm6295.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/n163/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/n163/n163.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/n163/n163.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/scc/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/scc/scc.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/scc/scc.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/template/template.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/template/template.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/vrcvi/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/vrcvi/vrcvi.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/vrcvi/vrcvi.hpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/x1_010/README.md (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/x1_010/x1_010.cpp (100%) rename {vgsound_emu-modified => extern/vgsound_emu-modified}/vgsound_emu/src/x1_010/x1_010.hpp (100%) diff --git a/vgsound_emu-modified/.clang-format b/extern/vgsound_emu-modified/.clang-format similarity index 100% rename from vgsound_emu-modified/.clang-format rename to extern/vgsound_emu-modified/.clang-format diff --git a/vgsound_emu-modified/.gitignore b/extern/vgsound_emu-modified/.gitignore similarity index 100% rename from vgsound_emu-modified/.gitignore rename to extern/vgsound_emu-modified/.gitignore diff --git a/vgsound_emu-modified/CHANGELOG.md b/extern/vgsound_emu-modified/CHANGELOG.md similarity index 100% rename from vgsound_emu-modified/CHANGELOG.md rename to extern/vgsound_emu-modified/CHANGELOG.md diff --git a/vgsound_emu-modified/CMakeLists.txt b/extern/vgsound_emu-modified/CMakeLists.txt similarity index 100% rename from vgsound_emu-modified/CMakeLists.txt rename to extern/vgsound_emu-modified/CMakeLists.txt diff --git a/vgsound_emu-modified/LICENSE b/extern/vgsound_emu-modified/LICENSE similarity index 100% rename from vgsound_emu-modified/LICENSE rename to extern/vgsound_emu-modified/LICENSE diff --git a/vgsound_emu-modified/MODIFIED.md b/extern/vgsound_emu-modified/MODIFIED.md similarity index 100% rename from vgsound_emu-modified/MODIFIED.md rename to extern/vgsound_emu-modified/MODIFIED.md diff --git a/vgsound_emu-modified/README.md b/extern/vgsound_emu-modified/README.md similarity index 100% rename from vgsound_emu-modified/README.md rename to extern/vgsound_emu-modified/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/core/util.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/util.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/core/util.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/core/util.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_alu.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/es550x/es550x_filter.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k005289/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/k005289/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/k007232/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k007232/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/k007232/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k007232/k007232.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k053260/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/k053260/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/msm6295/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/msm6295/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/msm6295/msm6295.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/n163/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/n163/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/n163/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/n163/n163.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/scc/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/template/template.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/template/template.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/template/template.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/template/template.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/template/template.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/template/template.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/template/template.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/template/template.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/vrcvi/vrcvi.hpp diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/x1_010/README.md rename to extern/vgsound_emu-modified/vgsound_emu/src/x1_010/README.md diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp rename to extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp diff --git a/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp similarity index 100% rename from vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp rename to extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.hpp From 74bf0171f0668e03a83a952897d0ef96a9d46b0c Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 12:36:36 +0900 Subject: [PATCH 14/63] Modifier disclaimer --- extern/vgsound_emu-modified/MODIFIED.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extern/vgsound_emu-modified/MODIFIED.md b/extern/vgsound_emu-modified/MODIFIED.md index 432d014f9..00a7e727d 100644 --- a/extern/vgsound_emu-modified/MODIFIED.md +++ b/extern/vgsound_emu-modified/MODIFIED.md @@ -5,3 +5,7 @@ this is a modified version of vgsound_emu emulation core library tailored for Fu it should not and shall NOT be mistaken for the original, authentic or actual version and revision of the library. you can get original software from [here](https://gitlab.com/cam900/vgsound_emu/). + +## Modifier + +- [cam900](https://gitlab.com/cam900) \ No newline at end of file From c9813192953959a6f821012cdf7df27c5b4a8e70 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 12:48:03 +0900 Subject: [PATCH 15/63] Sync with master --- extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp | 2 +- extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp index ef06c1511..0eb97ab00 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp @@ -16,7 +16,7 @@ void vox_core::vox_decoder_t::decoder_state_t::reset() } // copy from source -void vox_core::vox_decoder_t::decoder_state_t::copy(decoder_state_t src) +void vox_core::vox_decoder_t::decoder_state_t::copy_state(decoder_state_t src) { m_index = src.index(); m_step = src.step(); diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp index f3ae6d4c1..167d0fedd 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp @@ -42,14 +42,14 @@ class vox_core : public vgsound_emu_core decoder_state_t &operator=(decoder_state_t src) { - copy(src); + copy_state(src); return *this; } private: const bool m_wraparound = false; // wraparound or clamp? - void copy(decoder_state_t src); + void copy_state(decoder_state_t src); vox_core &m_vox; s8 m_index = 0; From 4eaf5ce9a6a74c8f2a59ccfc4980f47a7ff74ea8 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 12:55:05 +0900 Subject: [PATCH 16/63] Sync with master --- extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp | 2 +- extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp index 0eb97ab00..ec16ca2b0 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.cpp @@ -16,7 +16,7 @@ void vox_core::vox_decoder_t::decoder_state_t::reset() } // copy from source -void vox_core::vox_decoder_t::decoder_state_t::copy_state(decoder_state_t src) +void vox_core::vox_decoder_t::decoder_state_t::copy_state(decoder_state_t &src) { m_index = src.index(); m_step = src.step(); diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp index 167d0fedd..ead2ca753 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp @@ -40,7 +40,7 @@ class vox_core : public vgsound_emu_core s32 step() { return m_step; } - decoder_state_t &operator=(decoder_state_t src) + decoder_state_t &operator=(decoder_state_t &src) { copy_state(src); return *this; @@ -49,7 +49,7 @@ class vox_core : public vgsound_emu_core private: const bool m_wraparound = false; // wraparound or clamp? - void copy_state(decoder_state_t src); + void copy_state(decoder_state_t &src); vox_core &m_vox; s8 m_index = 0; From e9bdd356da4c9e7cc6742884cb9872b777f1c7f1 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 13:02:10 +0900 Subject: [PATCH 17/63] Sync with master --- extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp | 4 ++-- extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp index 34782f104..4c8454c88 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -35,7 +35,7 @@ class es5505_core : public es550x_shared_core inline s32 clamp16(s32 in) { return clamp(in, -0x8000, 0x7fff); } - inline void clamp16(output_t src) + inline void clamp16(output_t &src) { m_left = clamp16(src.left()); m_right = clamp16(src.right()); @@ -76,7 +76,7 @@ class es5505_core : public es550x_shared_core return *this; } - output_t &operator=(output_t src) + output_t &operator=(output_t &src) { clamp16(src); return *this; diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp index 84a6077de..53713cfbe 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -35,7 +35,7 @@ class es5506_core : public es550x_shared_core inline s32 clamp20(s32 in) { return clamp(in, -0x80000, 0x7ffff); } - inline void clamp20(output_t src) + inline void clamp20(output_t &src) { m_left = clamp20(src.left()); m_right = clamp20(src.right()); @@ -76,7 +76,7 @@ class es5506_core : public es550x_shared_core return *this; } - output_t &operator=(output_t src) + output_t &operator=(output_t &src) { clamp20(src); return *this; From 146da2ce7649fcea31f76502b4307461b1a70f23 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 13:05:58 +0900 Subject: [PATCH 18/63] Forgot to sync --- extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp | 4 ++-- extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp index 4c8454c88..d1c70614a 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -65,9 +65,9 @@ class es5505_core : public es550x_shared_core } // getters - inline u32 left() { return m_left; } + inline s32 left() { return m_left; } - inline u32 right() { return m_right; } + inline s32 right() { return m_right; } output_t &operator+=(output_t &src) { diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp index 53713cfbe..524e754e1 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -65,9 +65,9 @@ class es5506_core : public es550x_shared_core } // getters - inline u32 left() { return m_left; } + inline s32 left() { return m_left; } - inline u32 right() { return m_right; } + inline s32 right() { return m_right; } output_t &operator+=(output_t &src) { From 5af02d068e62a589f83fd787a646a3746f4d9a40 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 13:33:21 +0900 Subject: [PATCH 19/63] Sync with master --- .../vgsound_emu/src/es550x/es5504.cpp | 5 ++++- .../vgsound_emu/src/es550x/es5505.cpp | 8 ++++---- .../vgsound_emu/src/es550x/es5505.hpp | 4 ++-- .../vgsound_emu/src/es550x/es5506.cpp | 8 ++++---- .../vgsound_emu/src/es550x/es5506.hpp | 4 ++-- .../vgsound_emu/src/k053260/k053260.cpp | 4 ++-- .../vgsound_emu/src/k053260/k053260.hpp | 2 +- 7 files changed, 19 insertions(+), 16 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp index 586f15f30..b31a13b20 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5504.cpp @@ -357,7 +357,10 @@ void es5504_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access) if (bitfield(m_adc, 0)) // Writam_ble ADC { m_adc = (m_adc & 7) | (data & ~7); - m_intf.adc_w(m_adc & ~7); + if (cpu_access) + { + m_intf.adc_w(m_adc & ~7); + } } m_adc = (m_adc & ~3) | (data & 3); break; diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp index 07a7b7046..6b2712248 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp @@ -319,7 +319,7 @@ void es5505_core::host_w(u8 address, u16 data) m_hd = data; if (m_e.rising_edge()) { // update directly - write(m_ha, m_hd, true); + write(m_ha, m_hd); } else { @@ -330,9 +330,9 @@ void es5505_core::host_w(u8 address, u16 data) u16 es5505_core::read(u8 address, bool cpu_access) { return regs_r(m_page, address, cpu_access); } -void es5505_core::write(u8 address, u16 data, bool cpu_access) +void es5505_core::write(u8 address, u16 data) { - regs_w(m_page, address, data, cpu_access); + regs_w(m_page, address, data); } u16 es5505_core::regs_r(u8 page, u8 address, bool cpu_access) @@ -488,7 +488,7 @@ u16 es5505_core::regs_r(u8 page, u8 address, bool cpu_access) return ret; } -void es5505_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access) +void es5505_core::regs_w(u8 page, u8 address, u16 data) { address = bitfield(address, 0, 4); // 4 bit address for CPU access diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp index d1c70614a..4cbd25813 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -262,10 +262,10 @@ class es5505_core : public es550x_shared_core // bypass chips host interface for debug purpose only u16 read(u8 address, bool cpu_access = false); - void write(u8 address, u16 data, bool cpu_access = false); + void write(u8 address, u16 data); u16 regs_r(u8 page, u8 address, bool cpu_access = false); - void regs_w(u8 page, u8 address, u16 data, bool cpu_access = false); + void regs_w(u8 page, u8 address, u16 data); u16 regs_r(u8 page, u8 address) { diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp index ebb9a62d9..b0f887da5 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp @@ -438,7 +438,7 @@ void es5506_core::host_w(u8 address, u8 data) m_hd = data; if (m_e.rising_edge()) { // update directly - write(m_ha, m_hd, true); + write(m_ha, m_hd); } else { @@ -464,7 +464,7 @@ u8 es5506_core::read(u8 address, bool cpu_access) return bitfield(m_read_latch, 24, 8); } -void es5506_core::write(u8 address, u8 data, bool cpu_access) +void es5506_core::write(u8 address, u8 data) { const u8 byte = bitfield(address, 0, 2); // byte select const u8 shift = 24 - (byte << 3); @@ -478,7 +478,7 @@ void es5506_core::write(u8 address, u8 data, bool cpu_access) return; } - regs_w(m_page, address, m_write_latch, cpu_access); + regs_w(m_page, address, m_write_latch); // Reset latch m_write_latch = 0; @@ -707,7 +707,7 @@ u32 es5506_core::regs_r(u8 page, u8 address, bool cpu_access) return read_latch; } -void es5506_core::regs_w(u8 page, u8 address, u32 data, bool cpu_access) +void es5506_core::regs_w(u8 page, u8 address, u32 data) { // Global registers if (address >= 13) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp index 524e754e1..f54a03c3e 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -329,10 +329,10 @@ class es5506_core : public es550x_shared_core // bypass chips host interface for debug purpose only u8 read(u8 address, bool cpu_access = false); - void write(u8 address, u8 data, bool cpu_access = false); + void write(u8 address, u8 data); u32 regs_r(u8 page, u8 address, bool cpu_access = false); - void regs_w(u8 page, u8 address, u32 data, bool cpu_access = false); + void regs_w(u8 page, u8 address, u32 data); u8 regs8_r(u8 page, u8 address) { diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp index 6fe759ac2..cccdbbf58 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp @@ -15,7 +15,7 @@ void k053260_core::tick() { for (int i = 0; i < 4; i++) { - m_voice[i].tick(i); + m_voice[i].tick(); m_out[0] += m_voice[i].out(0); m_out[1] += m_voice[i].out(1); } @@ -36,7 +36,7 @@ void k053260_core::tick() m_dac.set_clock(bitfield(dac_clock, 0, 4)); } -void k053260_core::voice_t::tick(u8 ne) +void k053260_core::voice_t::tick() { if (m_enable && m_busy) { diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp index de39e35ec..bca78a1f4 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp @@ -61,7 +61,7 @@ class k053260_core : public vgsound_emu_core // internal state void reset(); - void tick(u8 ne); + void tick(); // accessors void write(u8 address, u8 data); From 81f812b2160063ce40f54571c8dd69c0c0f85914 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 13:56:09 +0900 Subject: [PATCH 20/63] Sync with master --- .../vgsound_emu/src/es550x/es5505.cpp | 9 ++++----- .../vgsound_emu/src/es550x/es5505.hpp | 20 +++++++------------ .../vgsound_emu/src/es550x/es5506.cpp | 10 +++++----- .../vgsound_emu/src/es550x/es5506.hpp | 20 +++++++------------ 4 files changed, 23 insertions(+), 36 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp index 6b2712248..6172a8665 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.cpp @@ -45,12 +45,11 @@ void es5505_core::tick() for (int i = 0; i < 4; i++) { // copy output - m_output[i] = m_output_temp[i]; - m_output_latch[i] = m_ch[i]; - m_output_temp[i].reset(); + m_output[i].copy_output(m_output_temp[i]); // clamp to 16 bit (upper 5 bits are overflow // guard bits) - m_output_latch[i].clamp16(); + m_output_latch[i].clamp16(m_ch[i]); + m_output_temp[i].reset(); // set signed if (m_output_latch[i].left() < 0) { @@ -155,7 +154,7 @@ void es5505_core::tick_perf() // output for (int c = 0; c < 4; c++) { - m_output[c] = m_ch[c]; + m_output[c].clamp16(m_ch[c]); } // update diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp index 4cbd25813..52a3b5f36 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp @@ -31,7 +31,13 @@ class es5505_core : public es550x_shared_core { m_left = 0; m_right = 0; - }; + } + + inline void copy_output(output_t &src) + { + m_left = src.left(); + m_right = src.right(); + } inline s32 clamp16(s32 in) { return clamp(in, -0x8000, 0x7fff); } @@ -76,18 +82,6 @@ class es5505_core : public es550x_shared_core return *this; } - output_t &operator=(output_t &src) - { - clamp16(src); - return *this; - } - - output_t &operator=(s32 val) - { - m_left = m_right = clamp16(val); - return *this; - } - output_t &operator>>(s32 shift) { m_left >>= shift; diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp index b0f887da5..83e43cac9 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.cpp @@ -63,11 +63,11 @@ void es5506_core::tick() for (int i = 0; i < 6; i++) { // copy output - m_output[i] = m_output_temp[i]; - m_output_latch[i] = m_ch[i]; - m_output_temp[i].reset(); + m_output[i].copy_output(m_output_temp[i]); // clamp to 20 bit (upper 3 bits are // overflow guard bits) + m_output_latch[i].clamp20(m_ch[i]); + m_output_temp[i].reset(); m_output_latch[i].clamp20(); // set signed if (m_output_latch[i].left() < 0) @@ -202,7 +202,7 @@ void es5506_core::tick_perf() { for (int c = 0; c < 6; c++) { - m_output[c] = m_ch[c] >> output_bits; + m_output[c].clamp20(m_ch[c] >> output_bits); } } } @@ -210,7 +210,7 @@ void es5506_core::tick_perf() { for (int c = 0; c < 6; c++) { - m_output[c] = 0; + m_output[c].reset(); } } diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp index f54a03c3e..b59a96dcf 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5506.hpp @@ -31,7 +31,13 @@ class es5506_core : public es550x_shared_core { m_left = 0; m_right = 0; - }; + } + + inline void copy_output(output_t &src) + { + m_left = src.left(); + m_right = src.right(); + } inline s32 clamp20(s32 in) { return clamp(in, -0x80000, 0x7ffff); } @@ -76,18 +82,6 @@ class es5506_core : public es550x_shared_core return *this; } - output_t &operator=(output_t &src) - { - clamp20(src); - return *this; - } - - output_t &operator=(s32 val) - { - m_left = m_right = clamp20(val); - return *this; - } - output_t &operator>>(s32 shift) { m_left >>= shift; From eeb09c703147a0845c7eb4886dfe60caac23b45b Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 17 Sep 2022 14:02:43 +0900 Subject: [PATCH 21/63] Sync with master --- .../vgsound_emu/src/core/vox/vox.hpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp index ead2ca753..231f9b638 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/core/vox/vox.hpp @@ -40,17 +40,11 @@ class vox_core : public vgsound_emu_core s32 step() { return m_step; } - decoder_state_t &operator=(decoder_state_t &src) - { - copy_state(src); - return *this; - } + void copy_state(decoder_state_t &src); private: const bool m_wraparound = false; // wraparound or clamp? - void copy_state(decoder_state_t &src); - vox_core &m_vox; s8 m_index = 0; s32 m_step = 16; @@ -76,7 +70,7 @@ class vox_core : public vgsound_emu_core { if (!m_loop_saved) { - m_loop = m_curr; + m_loop.copy_state(m_curr); m_loop_saved = true; } } @@ -85,7 +79,7 @@ class vox_core : public vgsound_emu_core { if (m_loop_saved) { - m_curr = m_loop; + m_curr.copy_state(m_loop); } } From 8ba53999bc710e51594c42e0c1d844f77474248c Mon Sep 17 00:00:00 2001 From: cam900 Date: Sun, 18 Sep 2022 12:09:54 +0900 Subject: [PATCH 22/63] Less confused naming --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 885a4c49e..634cf584f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -183,7 +183,7 @@ TAParamResult pVersion(String) { printf("- reSID by Dag Lem (GPLv2)\n"); printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n"); printf("- Stella by Stella Team (GPLv2)\n"); - printf("- vgsound_emu (second version) by cam900 (zlib)\n"); + printf("- vgsound_emu (second version) by cam900 (zlib license)\n"); return TA_PARAM_QUIT; } From 6bf6a854efc9d6d702699a48484e689396b3b993 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 17 Sep 2022 22:55:58 -0500 Subject: [PATCH 23/63] GUI: comfortable wave macro height --- src/gui/doAction.cpp | 3 +++ src/gui/gui.cpp | 1 + src/gui/gui.h | 6 ++++++ src/gui/insEdit.cpp | 2 +- 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 92e12a742..468b7e37c 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -645,6 +645,7 @@ void FurnaceGUI::doAction(int what) { } else { wantScrollList=true; MARK_MODIFIED; + RESET_WAVE_MACRO_ZOOM; } break; case GUI_ACTION_WAVE_LIST_DUPLICATE: @@ -657,6 +658,7 @@ void FurnaceGUI::doAction(int what) { (*e->song.wave[curWave])=(*e->song.wave[prevWave]); wantScrollList=true; MARK_MODIFIED; + RESET_WAVE_MACRO_ZOOM; } } break; @@ -1326,6 +1328,7 @@ void FurnaceGUI::doAction(int what) { } nextWindow=GUI_WINDOW_WAVE_EDIT; MARK_MODIFIED; + RESET_WAVE_MACRO_ZOOM; } } break; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5153e1f6a..06fc65f08 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3884,6 +3884,7 @@ bool FurnaceGUI::loop() { showError("cannot load wavetable! ("+e->getLastError()+")"); } else { MARK_MODIFIED; + RESET_WAVE_MACRO_ZOOM; } } break; diff --git a/src/gui/gui.h b/src/gui/gui.h index 1c8ee713f..6499c4d19 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -47,6 +47,12 @@ #define MARK_MODIFIED modified=true; #define WAKE_UP drawHalt=16; +#define RESET_WAVE_MACRO_ZOOM \ + for (DivInstrument* _wi: e->song.ins) { \ + _wi->std.waveMacro.vZoom=-1; \ + _wi->std.waveMacro.vScroll=-1; \ + } + #define BIND_FOR(x) getKeyName(actionKeys[x],true).c_str() // TODO: diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index b5347cbee..50fd286b8 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -4227,7 +4227,7 @@ void FurnaceGUI::drawInsEdit() { } const char* waveLabel="Waveform"; - int waveMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_VERA)?3:255; + int waveMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_VERA)?3:(MAX(1,e->song.waveLen-1)); bool bitMode=false; if (ins->type==DIV_INS_C64 || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SAA1099) { bitMode=true; From 635bcf1c6d461d938f667a6dc210cba21ddd5756 Mon Sep 17 00:00:00 2001 From: cam900 Date: Sun, 18 Sep 2022 13:19:08 +0900 Subject: [PATCH 24/63] Fix disclaimer --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 634cf584f..142454b81 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -183,7 +183,7 @@ TAParamResult pVersion(String) { printf("- reSID by Dag Lem (GPLv2)\n"); printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n"); printf("- Stella by Stella Team (GPLv2)\n"); - printf("- vgsound_emu (second version) by cam900 (zlib license)\n"); + printf("- vgsound_emu (second version, modified version) by cam900 (zlib license)\n"); return TA_PARAM_QUIT; } From ca224632a1109a960c691b0186be6f4824f7212c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 18 Sep 2022 00:26:51 -0500 Subject: [PATCH 25/63] further polish Namco 163 doc --- papers/doc/7-systems/n163.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/7-systems/n163.md b/papers/doc/7-systems/n163.md index 90f432289..efdc3c506 100644 --- a/papers/doc/7-systems/n163.md +++ b/papers/doc/7-systems/n163.md @@ -2,7 +2,7 @@ This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 256 nibbles (128 bytes) of internal RAM, and both channel registers and wavetables are stored here. Wavetables are variable in size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. At least 128 nibbles (64 bytes) can be dedicated to waves, with more available if not all channels are used - waveform RAM area becomes smaller as more channels are activated, since channel registers consume 8 bytes for each channel. You must avoid conflict with channel register area and waveform to avoid broken channel playback. -Namco 163 does not internally mix its channels. Because of that, only one channel can be output at the time, when multiple channels are used it will cycle between them, similarily to OPLL. Therefore, its sound quality gets more crunchy as more channels are activated. +Namco 163 uses time-division multiplexing for its output. this means that only one channel is output per sample (like OPLL and OPN2). therefore, its sound quality gets worse as more channels are activated. Furnace supports loading waveforms into RAM and waveform playback simultaneously, and channel limit is dynamically changeable with effect commands. You must load waveform to RAM first for playback, as its load behavior auto-updates when every waveform changes. From 75bcad558aa9058fbc0a4a2e27a6d3ed315a7046 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 18 Sep 2022 03:51:10 -0500 Subject: [PATCH 26/63] GUI: channel customization, part 1 --- TODO.md | 4 ++-- src/gui/gui.cpp | 32 ++++++++++++++++++++++++++++++++ src/gui/gui.h | 4 ++++ src/gui/guiConst.cpp | 2 ++ src/gui/pattern.cpp | 39 ++++++++++++++++++++++++++++++++------- src/gui/settings.cpp | 14 ++++++++------ 6 files changed, 80 insertions(+), 15 deletions(-) diff --git a/TODO.md b/TODO.md index 859ad68c2..a0da2bbdd 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,6 @@ - stereo separation control for AY - "paste with instrument" -- FM operator muting -- FM operator swap +- channel appearance settings +- auto-detect system - bug fixes diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 06fc65f08..40869c425 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -584,6 +584,38 @@ void FurnaceGUI::updateWindowTitle() { if (sdlWin!=NULL) SDL_SetWindowTitle(sdlWin,title.c_str()); } +ImVec4 FurnaceGUI::channelColor(int ch) { + switch (settings.channelColors) { + case 0: + return uiColors[GUI_COLOR_CHANNEL_BG]; + break; + case 1: + return uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(ch)]; + break; + case 2: + return uiColors[GUI_COLOR_INSTR_STD+e->getPreferInsType(ch)]; + break; + } + // invalid + return uiColors[GUI_COLOR_TEXT]; +} + +ImVec4 FurnaceGUI::channelTextColor(int ch) { + switch (settings.channelTextColors) { + case 0: + return uiColors[GUI_COLOR_CHANNEL_FG]; + break; + case 1: + return uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(ch)]; + break; + case 2: + return uiColors[GUI_COLOR_INSTR_STD+e->getPreferInsType(ch)]; + break; + } + // invalid + return uiColors[GUI_COLOR_TEXT]; +} + const char* defaultLayout="[Window][DockSpaceViewport_11111111]\n\ Pos=0,24\n\ Size=1280,731\n\ diff --git a/src/gui/gui.h b/src/gui/gui.h index 6499c4d19..b9784f3f6 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -161,6 +161,8 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_OPL_DRUMS, GUI_COLOR_INSTR_UNKNOWN, + GUI_COLOR_CHANNEL_BG, + GUI_COLOR_CHANNEL_FG, GUI_COLOR_CHANNEL_FM, GUI_COLOR_CHANNEL_PULSE, GUI_COLOR_CHANNEL_NOISE, @@ -1604,6 +1606,8 @@ class FurnaceGUI { void updateWindowTitle(); void prepareLayout(); + ImVec4 channelColor(int ch); + ImVec4 channelTextColor(int ch); void readOsc(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index e79de4106..7c987358e 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -774,6 +774,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_OPL_DRUMS,"",ImVec4(0.3f,1.0f,0.9f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), + D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), + D(GUI_COLOR_CHANNEL_FG,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), D(GUI_COLOR_CHANNEL_FM,"",ImVec4(0.2f,0.8f,1.0f,1.0f)), D(GUI_COLOR_CHANNEL_PULSE,"",ImVec4(0.4f,1.0f,0.2f,1.0f)), D(GUI_COLOR_CHANNEL_NOISE,"",ImVec4(0.8f,0.8f,0.8f,1.0f)), diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index ec65101df..e6bbc72fa 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -468,20 +468,44 @@ void FurnaceGUI::drawPattern() { snprintf(chanID,2048," %s##_CH%d",chName,i); } } + bool muted=e->isChannelMuted(i); - ImVec4 chanHead=muted?uiColors[GUI_COLOR_CHANNEL_MUTED]:uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(i)]; + ImVec4 chanHead=muted?uiColors[GUI_COLOR_CHANNEL_MUTED]:channelColor(i); ImVec4 chanHeadActive=chanHead; ImVec4 chanHeadHover=chanHead; + if (e->keyHit[i]) { - keyHit[i]=0.2; - if (!muted) { - int note=e->getChanState(i)->note+60; - if (note>=0 && note<180) { - pianoKeyHit[note]=1.0; + if (settings.channelFeedbackStyle==1) { + keyHit[i]=0.2; + if (!muted) { + int note=e->getChanState(i)->note+60; + if (note>=0 && note<180) { + pianoKeyHit[note]=1.0; + } } } e->keyHit[i]=false; } + if (settings.channelFeedbackStyle==2 && e->isRunning()) { + float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i)); + if (!e->getChanState(i)->keyOn) amount=0.0f; + keyHit[i]=amount*0.2f; + if (!muted) { + int note=e->getChanState(i)->note+60; + if (note>=0 && note<180) { + pianoKeyHit[note]=amount; + } + } + } else if (settings.channelFeedbackStyle==3 && e->isRunning()) { + bool active=e->getChanState(i)->keyOn; + keyHit[i]=active?0.2f:0.0f; + if (!muted) { + int note=e->getChanState(i)->note+60; + if (note>=0 && note<180) { + pianoKeyHit[note]=active?1.0f:0.0f; + } + } + } if (settings.guiColorsBase) { chanHead.x*=1.0-keyHit[i]; chanHead.y*=1.0-keyHit[i]; chanHead.z*=1.0-keyHit[i]; chanHeadActive.x*=0.5; chanHeadActive.y*=0.5; chanHeadActive.z*=0.5; @@ -496,6 +520,7 @@ void FurnaceGUI::drawPattern() { ImGui::PushStyleColor(ImGuiCol_Header,chanHead); ImGui::PushStyleColor(ImGuiCol_HeaderActive,chanHeadActive); ImGui::PushStyleColor(ImGuiCol_HeaderHovered,chanHeadHover); + ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(channelTextColor(i))); ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(chanHead)); if (muted) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_CHANNEL_MUTED]); ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale)); @@ -513,7 +538,7 @@ void FurnaceGUI::drawPattern() { } } if (muted) ImGui::PopStyleColor(); - ImGui::PopStyleColor(3); + ImGui::PopStyleColor(4); if (settings.soloAction!=2) if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { inhibitMenu=true; e->toggleSolo(i); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 54da8e893..1bb946522 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1284,14 +1284,14 @@ void FurnaceGUI::drawSettings() { } ImGui::Text("Channel name colors:"); - if (ImGui::RadioButton("Single##CTC0",settings.channelColors==0)) { - settings.channelColors=0; + if (ImGui::RadioButton("Single##CTC0",settings.channelTextColors==0)) { + settings.channelTextColors=0; } - if (ImGui::RadioButton("Channel type##CTC1",settings.channelColors==1)) { - settings.channelColors=1; + if (ImGui::RadioButton("Channel type##CTC1",settings.channelTextColors==1)) { + settings.channelTextColors=1; } - if (ImGui::RadioButton("Instrument type##CTC2",settings.channelColors==2)) { - settings.channelColors=2; + if (ImGui::RadioButton("Instrument type##CTC2",settings.channelTextColors==2)) { + settings.channelTextColors=2; } ImGui::Text("Channel style:"); @@ -1659,6 +1659,8 @@ void FurnaceGUI::drawSettings() { ImGui::TreePop(); } if (ImGui::TreeNode("Channel")) { + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_BG,"Single color (background)"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FG,"Single color (text)"); UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FM,"FM"); UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PULSE,"Pulse"); UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_NOISE,"Noise"); From ebb939c1896d4f0c5f075b0e61920d806e1000b9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 18 Sep 2022 04:20:08 -0500 Subject: [PATCH 27/63] GUI: add channel font option --- src/gui/gui.h | 2 ++ src/gui/pattern.cpp | 5 +++++ src/gui/settings.cpp | 12 ++++++++++++ 3 files changed, 19 insertions(+) diff --git a/src/gui/gui.h b/src/gui/gui.h index b9784f3f6..09155f9be 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1178,6 +1178,7 @@ class FurnaceGUI { int channelStyle; int channelVolStyle; int channelFeedbackStyle; + int channelFont; int maxRecentFile; unsigned int maxUndoSteps; String mainFontPath; @@ -1299,6 +1300,7 @@ class FurnaceGUI { channelStyle(0), channelVolStyle(0), channelFeedbackStyle(1), + channelFont(1), maxRecentFile(10), maxUndoSteps(100), mainFontPath(""), diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index e6bbc72fa..4fbb99977 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -523,10 +523,15 @@ void FurnaceGUI::drawPattern() { ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(channelTextColor(i))); ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(chanHead)); if (muted) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_CHANNEL_MUTED]); + if (settings.channelFont==0) ImGui::PushFont(mainFont); + + // TODO: appearance ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale)); + if (displayTooltip && ImGui::IsItemHovered()) { ImGui::SetTooltip("%s",e->getChannelName(i)); } + if (settings.channelFont==0) ImGui::PopFont(); if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { if (settings.soloAction!=1 && soloTimeout>0 && soloChan==i) { e->toggleSolo(i); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 1bb946522..c771f6a16 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1343,6 +1343,15 @@ void FurnaceGUI::drawSettings() { settings.channelFeedbackStyle=3; } + ImGui::Text("Channel font:"); + + if (ImGui::RadioButton("Regular##CHFont0",settings.channelFont==0)) { + settings.channelFont=0; + } + if (ImGui::RadioButton("Monospace##CHFont1",settings.channelFont==1)) { + settings.channelFont=1; + } + ImGui::Separator(); bool insEditColorizeB=settings.insEditColorize; @@ -2282,6 +2291,7 @@ void FurnaceGUI::syncSettings() { settings.channelStyle=e->getConfInt("channelStyle",0); settings.channelVolStyle=e->getConfInt("channelVolStyle",0); settings.channelFeedbackStyle=e->getConfInt("channelFeedbackStyle",1); + settings.channelFont=e->getConfInt("channelFont",1); settings.maxRecentFile=e->getConfInt("maxRecentFile",10); clampSetting(settings.mainFontSize,2,96); @@ -2381,6 +2391,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.channelStyle,0,5); clampSetting(settings.channelVolStyle,0,3); clampSetting(settings.channelFeedbackStyle,0,3); + clampSetting(settings.channelFont,0,1); clampSetting(settings.maxRecentFile,0,30); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); @@ -2536,6 +2547,7 @@ void FurnaceGUI::commitSettings() { e->setConf("channelStyle",settings.channelStyle); e->setConf("channelVolStyle",settings.channelVolStyle); e->setConf("channelFeedbackStyle",settings.channelFeedbackStyle); + e->setConf("channelFont",settings.channelFont); e->setConf("maxRecentFile",settings.maxRecentFile); // colors From 3cb1571fb6c85794d19ec3c9f459f9113fb57a04 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 20 Sep 2022 01:00:31 -0500 Subject: [PATCH 28/63] GUI: implement channel style settings --- extern/imgui_patched/imgui.h | 2 + extern/imgui_patched/imgui_tables.cpp | 2 +- src/gui/pattern.cpp | 135 +++++++++++++++++++++++++- src/gui/sysEx.cpp | 1 + 4 files changed, 135 insertions(+), 5 deletions(-) diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index 7249c5402..e9ccea9b1 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -1220,6 +1220,8 @@ enum ImGuiTableFlags_ // Sorting ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + // tildearrow + ImGuiTableFlags_NoBordersInFrozenArea = 1 << 28, // Disable vertical borders in frozen area. // [Internal] Combinations and masks ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame diff --git a/extern/imgui_patched/imgui_tables.cpp b/extern/imgui_patched/imgui_tables.cpp index 5a21a0b13..46bf0b132 100644 --- a/extern/imgui_patched/imgui_tables.cpp +++ b/extern/imgui_patched/imgui_tables.cpp @@ -2532,7 +2532,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Draw inner border and resizing feedback ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); const float border_size = TABLE_BORDER_SIZE; - const float draw_y1 = table->InnerRect.Min.y; + const float draw_y1 = table->InnerRect.Min.y + ((table->Flags & ImGuiTableFlags_NoBordersInFrozenArea)?table_instance->LastFirstRowHeight:0.0f); const float draw_y2_body = table->InnerRect.Max.y; const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastFirstRowHeight) : draw_y1; if (table->Flags & ImGuiTableFlags_BordersInnerV) diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 4fbb99977..b986cedc6 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +// for suck's fake Clang extension! #define _USE_MATH_DEFINES #include "gui.h" #include "../ta-log.h" @@ -400,7 +401,7 @@ void FurnaceGUI::drawPattern() { ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]); ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_SELECTION_HOVER]); ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_SELECTION_ACTIVE]); - if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX)) { + if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea)) { ImGui::TableSetupColumn("pos",ImGuiTableColumnFlags_WidthFixed); char chanID[2048]; float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale); @@ -473,6 +474,7 @@ void FurnaceGUI::drawPattern() { ImVec4 chanHead=muted?uiColors[GUI_COLOR_CHANNEL_MUTED]:channelColor(i); ImVec4 chanHeadActive=chanHead; ImVec4 chanHeadHover=chanHead; + ImVec4 chanHeadBase=chanHead; if (e->keyHit[i]) { if (settings.channelFeedbackStyle==1) { @@ -515,18 +517,140 @@ void FurnaceGUI::drawPattern() { chanHeadActive.x*=0.8; chanHeadActive.y*=0.8; chanHeadActive.z*=0.8; chanHeadHover.x*=0.4+keyHit[i]; chanHeadHover.y*=0.4+keyHit[i]; chanHeadHover.z*=0.4+keyHit[i]; } - keyHit[i]-=0.02*60.0*ImGui::GetIO().DeltaTime; + keyHit[i]-=((settings.channelStyle==0)?0.02:0.01)*60.0*ImGui::GetIO().DeltaTime; if (keyHit[i]<0) keyHit[i]=0; ImGui::PushStyleColor(ImGuiCol_Header,chanHead); ImGui::PushStyleColor(ImGuiCol_HeaderActive,chanHeadActive); ImGui::PushStyleColor(ImGuiCol_HeaderHovered,chanHeadHover); ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(channelTextColor(i))); - ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(chanHead)); + if (settings.channelStyle==0) ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(chanHead)); if (muted) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_CHANNEL_MUTED]); if (settings.channelFont==0) ImGui::PushFont(mainFont); // TODO: appearance - ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale)); + ImGuiWindow* window=ImGui::GetCurrentWindow(); + ImVec2 size=ImVec2( + 1.0f, + lineHeight+1.0f*dpiScale+6.0*dpiScale + ); + ImDrawList* dl=ImGui::GetWindowDrawList(); + + if (settings.channelStyle==2) { + size.y+=6.0f*dpiScale; + } + + ImVec2 minArea=window->DC.CursorPos; + ImVec2 maxArea=ImVec2( + minArea.x+window->WorkRect.Max.x-window->WorkRect.Min.x, + minArea.y+size.y + ); + ImRect rect=ImRect(minArea,maxArea); + switch (settings.channelStyle) { + case 0: // classic + ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale)); + break; + case 1: { // line + ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + ImU32 fadeCol0=ImGui::GetColorU32(ImVec4( + chanHeadBase.x, + chanHeadBase.y, + chanHeadBase.z, + hovered?0.25f:0.0f + )); + ImU32 fadeCol=ImGui::GetColorU32(ImVec4( + chanHeadBase.x, + chanHeadBase.y, + chanHeadBase.z, + hovered?0.5f:MIN(1.0f,chanHeadBase.w*keyHit[i]*4.0f) + )); + dl->AddRectFilledMultiColor(rect.Min,rect.Max,fadeCol0,fadeCol0,fadeCol,fadeCol); + dl->AddLine(ImVec2(rect.Min.x,rect.Max.y),ImVec2(rect.Max.x,rect.Max.y),ImGui::GetColorU32(chanHeadBase),2.0f*dpiScale); + dl->AddText(ImVec2(rect.Min.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID,strstr(chanID,"##")); + } + break; + } + case 2: { // round + ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + ImU32 fadeCol0=ImGui::GetColorU32(ImVec4( + chanHeadBase.x, + chanHeadBase.y, + chanHeadBase.z, + hovered?0.5f:MIN(1.0f,0.3f+chanHeadBase.w*keyHit[i]*1.5f) + )); + ImU32 fadeCol=ImGui::GetColorU32(ImVec4( + chanHeadBase.x, + chanHeadBase.y, + chanHeadBase.z, + hovered?0.3f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*1.2f) + )); + ImVec2 rMin=rect.Min; + ImVec2 rMax=rect.Max; + rMin.x+=3.0f*dpiScale; + rMin.y+=6.0f*dpiScale; + rMax.x-=3.0f*dpiScale; + rMax.y-=6.0f*dpiScale; + dl->AddRectFilledMultiColor(rMin,rMax,fadeCol0,fadeCol0,fadeCol,fadeCol,4.0f*dpiScale); + dl->AddText(ImVec2(rect.Min.x,rect.Min.y+6.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID,strstr(chanID,"##")); + } + break; + } + case 3: // split button + ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale)); + ImGui::TextUnformatted(chanID,strstr(chanID,"##")); + ImGui::SameLine(); + ImGui::PushFont(mainFont); + ImGui::PushID(chanID); + ImGui::SmallButton(muted?ICON_FA_VOLUME_OFF:ICON_FA_VOLUME_UP); + ImGui::PopID(); + ImGui::PopFont(); + break; + case 4: { // square border + ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + ImU32 fadeCol=ImGui::GetColorU32(ImVec4( + chanHeadBase.x, + chanHeadBase.y, + chanHeadBase.z, + hovered?1.0f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*4.0f) + )); + ImVec2 rMin=rect.Min; + ImVec2 rMax=rect.Max; + rMin.x+=2.0f*dpiScale; + rMin.y+=3.0f*dpiScale; + rMax.x-=3.0f*dpiScale; + rMax.y-=3.0f*dpiScale; + dl->AddRect(rMin,rMax,fadeCol,0.0f,2.0*dpiScale); + dl->AddText(ImVec2(rect.Min.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID,strstr(chanID,"##")); + } + break; + } + case 5: { // round border + ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) { + bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID)); + ImU32 fadeCol=ImGui::GetColorU32(ImVec4( + chanHeadBase.x, + chanHeadBase.y, + chanHeadBase.z, + hovered?1.0f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*4.0f) + )); + ImVec2 rMin=rect.Min; + ImVec2 rMax=rect.Max; + rMin.x+=2.0f*dpiScale; + rMin.y+=3.0f*dpiScale; + rMax.x-=3.0f*dpiScale; + rMax.y-=3.0f*dpiScale; + dl->AddRect(rMin,rMax,fadeCol,4.0f*dpiScale,ImDrawFlags_RoundCornersAll,2.0*dpiScale); + dl->AddText(ImVec2(rect.Min.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID,strstr(chanID,"##")); + } + break; + } + } if (displayTooltip && ImGui::IsItemHovered()) { ImGui::SetTooltip("%s",e->getChannelName(i)); @@ -548,6 +672,9 @@ void FurnaceGUI::drawPattern() { inhibitMenu=true; e->toggleSolo(i); } + if (settings.channelStyle==3) { + ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale)); + } if (extraChannelButtons==2) { DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true); ImGui::PushFont(mainFont); diff --git a/src/gui/sysEx.cpp b/src/gui/sysEx.cpp index f8849b562..5a72649ee 100644 --- a/src/gui/sysEx.cpp +++ b/src/gui/sysEx.cpp @@ -60,6 +60,7 @@ bool FurnaceGUI::parseSysEx(unsigned char* data, size_t len) { op.rs=reader.readC(); reader.readC(); // EBS - ignore op.am=reader.readC(); + // TODO: don't ignore after I add KVS to Furnace reader.readC(); // KVS - ignore op.tl=3+((99-reader.readC())*124)/99; unsigned char freq=reader.readC(); From 1a84812a1ddfaac14be9a294d743b71c99e6b82b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 20 Sep 2022 01:03:44 -0500 Subject: [PATCH 29/63] OPNA: fix forceIns RSS/ADPCM volume --- src/engine/platform/ym2608.cpp | 5 +++++ src/engine/platform/ym2608ext.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index ec90c1c6b..42bea41b2 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -1114,6 +1114,11 @@ void DivPlatformYM2608::forceIns() { } for (int i=9; i<16; i++) { chan[i].insChanged=true; + if (i>14) { // ADPCM-B + immWrite(0x10b,chan[i].outVol); + } else { + immWrite(0x18+(i-9),isMuted[i]?0:((chan[i].pan<<6)|chan[i].vol)); + } } ay->forceIns(); diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index c6d7e03b6..116f6b95c 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -472,8 +472,13 @@ void DivPlatformYM2608Ext::forceIns() { chan[i].freqChanged=true; } } - for (int i=6; i<16; i++) { + for (int i=9; i<16; i++) { chan[i].insChanged=true; + if (i>14) { // ADPCM-B + immWrite(0x10b,chan[i].outVol); + } else { + immWrite(0x18+(i-9),isMuted[i]?0:((chan[i].pan<<6)|chan[i].vol)); + } } ay->forceIns(); ay->flushWrites(); From e1890173b266efa2bb95d6648eb21f4f2deaf077 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 20 Sep 2022 02:32:23 -0500 Subject: [PATCH 30/63] GUI: implement channel volume bar --- TODO.md | 1 - src/engine/platform/ay.cpp | 2 +- src/engine/platform/ay8930.cpp | 2 +- src/engine/platform/sms.cpp | 4 +-- src/gui/chanOsc.cpp | 34 +++++++++++++++++++- src/gui/gui.cpp | 8 +++++ src/gui/gui.h | 2 ++ src/gui/pattern.cpp | 58 +++++++++++++++++++++++++++++++++- src/gui/settings.cpp | 5 ++- 9 files changed, 108 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index a0da2bbdd..028f21077 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,5 @@ - stereo separation control for AY - "paste with instrument" -- channel appearance settings - auto-detect system - bug fixes diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 23f20c2a0..f475b4750 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -126,7 +126,7 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l } for (int ch=0; ch<3; ch++) { for (size_t i=0; idata[oscBuf[ch]->needle++]=ayBuf[ch][i]; + oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i]<<2; } } } diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index d77457d66..123dc68ac 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -111,7 +111,7 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l for (int ch=0; ch<3; ch++) { for (size_t i=0; idata[oscBuf[ch]->needle++]=ayBuf[ch][i]; + oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i]<<2; } } } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index d4d77f171..e4d408f7b 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -78,7 +78,7 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_ if (isMuted[i]) { oscBuf[i]->data[oscBuf[i]->needle++]=0; } else { - oscBuf[i]->data[oscBuf[i]->needle++]=sn_nuked.vol_table[sn_nuked.volume_out[i]]; + oscBuf[i]->data[oscBuf[i]->needle++]=sn_nuked.vol_table[sn_nuked.volume_out[i]]*3; } } } @@ -104,7 +104,7 @@ void DivPlatformSMS::acquire_mame(short* bufL, short* bufR, size_t start, size_t if (isMuted[i]) { oscBuf[i]->data[oscBuf[i]->needle++]=0; } else { - oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i); + oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i)*3; } } } diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 547e59a17..08174b0b6 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -69,6 +69,39 @@ float FurnaceGUI::computeGradPos(int type, int chan) { return 0.0f; } +void FurnaceGUI::calcChanOsc() { + std::vector oscBufs; + std::vector oscFFTs; + std::vector oscChans; + + int chans=e->getTotalChannelCount(); + + for (int i=0; igetOscBuffer(i); + if (buf!=NULL && e->curSubSong->chanShow[i]) { + // 30ms should be enough + int displaySize=(float)(buf->rate)*0.03f; + if (e->isRunning()) { + float minLevel=1.0f; + float maxLevel=-1.0f; + unsigned short needlePos=buf->needle; + needlePos-=displaySize; + for (unsigned short i=0; i<512; i++) { + float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f; + if (minLevel>y) minLevel=y; + if (maxLevel1.0f) estimate=1.0f; + chanOscVol[i]=MAX(chanOscVol[i]*0.87f,estimate); + } + } else { + chanOscVol[i]=MAX(chanOscVol[i]*0.87f,0.0f); + } + if (chanOscVol[i]<0.00001f) chanOscVol[i]=0.0f; + } +} + void FurnaceGUI::drawChanOsc() { if (nextWindow==GUI_WINDOW_CHAN_OSC) { chanOscOpen=true; @@ -361,7 +394,6 @@ void FurnaceGUI::drawChanOsc() { if (maxLeveldata[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 40869c425..d42ec0f88 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3471,6 +3471,8 @@ bool FurnaceGUI::loop() { ImGui::EndMainMenuBar(); } + calcChanOsc(); + if (mobileUI) { globalWinFlags=ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoBringToFrontOnFocus; //globalWinFlags=ImGuiWindowFlags_NoTitleBar; @@ -3546,6 +3548,12 @@ bool FurnaceGUI::loop() { drawEffectList(); } + for (int i=0; igetTotalChannelCount(); i++) { + if (e->keyHit[i]) { + e->keyHit[i]=false; + } + } + if (inspectorOpen) ImGui::ShowMetricsWindow(&inspectorOpen); if (firstFrame) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 09155f9be..bbfb3cf5b 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1558,6 +1558,7 @@ class FurnaceGUI { // visualizer float keyHit[DIV_MAX_CHANS]; + float keyHit1[DIV_MAX_CHANS]; int lastIns[DIV_MAX_CHANS]; // log window @@ -1612,6 +1613,7 @@ class FurnaceGUI { ImVec4 channelTextColor(int ch); void readOsc(); + void calcChanOsc(); void pushAccentColors(const ImVec4& one, const ImVec4& two, const ImVec4& border, const ImVec4& borderShadow); void popAccentColors(); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index b986cedc6..f35f828c0 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -486,7 +486,6 @@ void FurnaceGUI::drawPattern() { } } } - e->keyHit[i]=false; } if (settings.channelFeedbackStyle==2 && e->isRunning()) { float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i)); @@ -547,6 +546,10 @@ void FurnaceGUI::drawPattern() { ImRect rect=ImRect(minArea,maxArea); switch (settings.channelStyle) { case 0: // classic + if (settings.channelVolStyle!=0) { + // sorry... + ImGui::Dummy(ImVec2(dpiScale,dpiScale)); + } ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale)); break; case 1: { // line @@ -672,9 +675,62 @@ void FurnaceGUI::drawPattern() { inhibitMenu=true; e->toggleSolo(i); } + if (settings.channelStyle==3) { ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale)); } + + // volume bar + if (settings.channelVolStyle!=0) { + ImVec2 sizeV=ImVec2( + 1.0f, + 6.0*dpiScale + ); + ImVec2 minAreaV=window->DC.CursorPos; + ImVec2 maxAreaV=ImVec2( + minAreaV.x+window->WorkRect.Max.x-window->WorkRect.Min.x, + minAreaV.y+sizeV.y + ); + ImRect rectV=ImRect(minAreaV,maxAreaV); + ImGui::ItemSize(sizeV,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rectV,ImGui::GetID(chanID))) { + float xLeft=0.0f; + float xRight=1.0f; + + if (e->keyHit[i]) { + keyHit1[i]=1.0f; + } + + if (e->isRunning()) { + switch (settings.channelVolStyle) { + case 1: // simple + xRight=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.9+(keyHit1[i]*0.1f); + break; + case 2: // stereo + xRight=0.5+((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.4+(keyHit1[i]*0.1f); + xLeft=1.0-xRight; + break; + case 3: // real + xRight=chanOscVol[i]; + break; + case 4: // real (stereo) + xRight=0.5+chanOscVol[i]*0.5; + xLeft=1.0-xRight; + break; + } + + dl->AddRectFilled( + ImLerp(rectV.Min,rectV.Max,ImVec2(xLeft,0.0f)), + ImLerp(rectV.Min,rectV.Max,ImVec2(xRight,1.0f)), + ImGui::GetColorU32(chanHeadBase) + ); + } + keyHit1[i]-=0.2f; + if (keyHit1[i]<0.0f) keyHit1[i]=0.0f; + } + } + + // extra buttons if (extraChannelButtons==2) { DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true); ImGui::PushFont(mainFont); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index c771f6a16..e5c609829 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1327,6 +1327,9 @@ void FurnaceGUI::drawSettings() { if (ImGui::RadioButton("Real##CHV3",settings.channelVolStyle==3)) { settings.channelVolStyle=3; } + if (ImGui::RadioButton("Real (stereo)##CHV4",settings.channelVolStyle==4)) { + settings.channelVolStyle=4; + } ImGui::Text("Channel feedback style:"); @@ -2389,7 +2392,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.channelColors,0,2); clampSetting(settings.channelTextColors,0,2); clampSetting(settings.channelStyle,0,5); - clampSetting(settings.channelVolStyle,0,3); + clampSetting(settings.channelVolStyle,0,4); clampSetting(settings.channelFeedbackStyle,0,3); clampSetting(settings.channelFont,0,1); clampSetting(settings.maxRecentFile,0,30); From a58529a49b4dcd8728d5e73472476e99e5febf27 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 20 Sep 2022 02:57:56 -0500 Subject: [PATCH 31/63] GUI: fix some thread problems with keyHit --- src/gui/chanOsc.cpp | 2 +- src/gui/gui.cpp | 9 +++------ src/gui/pattern.cpp | 3 +++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 08174b0b6..89b89801b 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -63,7 +63,7 @@ float FurnaceGUI::computeGradPos(int type, int chan) { return chanOscBright[chan]; break; case GUI_OSCREF_NOTE_TRIGGER: - return keyHit[chan]*5.0f; + return keyHit1[chan]; break; } return 0.0f; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index d42ec0f88..a2eb9c920 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3548,12 +3548,6 @@ bool FurnaceGUI::loop() { drawEffectList(); } - for (int i=0; igetTotalChannelCount(); i++) { - if (e->keyHit[i]) { - e->keyHit[i]=false; - } - } - if (inspectorOpen) ImGui::ShowMetricsWindow(&inspectorOpen); if (firstFrame) { @@ -5462,6 +5456,9 @@ FurnaceGUI::FurnaceGUI(): waveGenFMCon2[0]=true; waveGenFMCon3[0]=true; + memset(keyHit,0,sizeof(float)*DIV_MAX_CHANS); + memset(keyHit1,0,sizeof(float)*DIV_MAX_CHANS); + memset(pianoKeyHit,0,sizeof(float)*180); memset(pianoKeyPressed,0,sizeof(bool)*180); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index f35f828c0..add285f64 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -477,6 +477,7 @@ void FurnaceGUI::drawPattern() { ImVec4 chanHeadBase=chanHead; if (e->keyHit[i]) { + keyHit1[i]=1.0f; if (settings.channelFeedbackStyle==1) { keyHit[i]=0.2; if (!muted) { @@ -486,6 +487,7 @@ void FurnaceGUI::drawPattern() { } } } + e->keyHit[i]=false; } if (settings.channelFeedbackStyle==2 && e->isRunning()) { float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i)); @@ -699,6 +701,7 @@ void FurnaceGUI::drawPattern() { if (e->keyHit[i]) { keyHit1[i]=1.0f; + e->keyHit[i]=false; } if (e->isRunning()) { From 980f970809cd26a810f6236e30741ba2e352f0de Mon Sep 17 00:00:00 2001 From: freq-mod <32672779+freq-mod@users.noreply.github.com> Date: Tue, 20 Sep 2022 21:13:29 +0000 Subject: [PATCH 32/63] fix opll bbc micro clock, improve some presets --- src/gui/presets.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++-- src/gui/sysConf.cpp | 2 +- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 1bd637a23..da1c4d51f 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -960,11 +960,27 @@ void FurnaceGUI::initSystemPresets() { cat.systems.push_back(FurnaceGUISysDef( "NEC PC-98 (with PC-9801-86)", { // -73 also has OPNA DIV_SYSTEM_PC98, 64, 0, 1, + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16), // 2x 16-bit Burr Brown DAC + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16), 0 } )); cat.systems.push_back(FurnaceGUISysDef( "NEC PC-98 (with PC-9801-86; extended channel 3)", { // -73 also has OPNA + DIV_SYSTEM_PC98_EXT, 64, 0, 1, + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16), + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16), + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NEC PC-98 (with PC-9801-73)", { + DIV_SYSTEM_PC98, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NEC PC-98 (with PC-9801-73; extended channel 3)", { DIV_SYSTEM_PC98_EXT, 64, 0, 1, 0 } @@ -972,6 +988,7 @@ void FurnaceGUI::initSystemPresets() { cat.systems.push_back(FurnaceGUISysDef( "NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible)", { DIV_SYSTEM_OPN, 64, 0, 2, // 4MHz + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20), DIV_SYSTEM_OPL3, 64, 0, 0, 0 } @@ -979,6 +996,7 @@ void FurnaceGUI::initSystemPresets() { cat.systems.push_back(FurnaceGUISysDef( "NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible; extended channel 3)", { DIV_SYSTEM_OPN_EXT, 64, 0, 2, // 4MHz + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20), DIV_SYSTEM_OPL3, 64, 0, 0, 0 } @@ -986,6 +1004,7 @@ void FurnaceGUI::initSystemPresets() { cat.systems.push_back(FurnaceGUISysDef( "NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible in drums mode)", { DIV_SYSTEM_OPN, 64, 0, 2, // 4MHz + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20), DIV_SYSTEM_OPL3_DRUMS, 64, 0, 2, 0 } @@ -993,6 +1012,7 @@ void FurnaceGUI::initSystemPresets() { cat.systems.push_back(FurnaceGUISysDef( "NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible in drums mode; extended channel 3)", { DIV_SYSTEM_OPN_EXT, 64, 0, 2, // 4MHz + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20), DIV_SYSTEM_OPL3_DRUMS, 64, 0, 2, 0 } @@ -1092,6 +1112,20 @@ void FurnaceGUI::initSystemPresets() { 0 } )); + cat.systems.push_back(FurnaceGUISysDef( + "Atari ST", { + DIV_SYSTEM_AY8910, 64, 0, 3, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Atari STE", { + DIV_SYSTEM_AY8910, 64, 0, 3, + DIV_SYSTEM_PCM_DAC, 64, 0, 50667|(7<<16), + DIV_SYSTEM_PCM_DAC, 64, 0, 50667|(7<<16), + 0 + } + )); cat.systems.push_back(FurnaceGUISysDef( "SAM Coupé", { DIV_SYSTEM_SAA1099, 64, 0, 0, @@ -1146,17 +1180,33 @@ void FurnaceGUI::initSystemPresets() { 0 } )); - cat.systems.push_back(FurnaceGUISysDef( - "PC + AdLib/Sound Blaster", { + cat.systems.push_back(FurnaceGUISysDef( + "PC + AdLib", { DIV_SYSTEM_OPL2, 64, 0, 0, DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + AdLib (drums mode)", { + DIV_SYSTEM_OPL2, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster", { + DIV_SYSTEM_OPL2, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16), + 0 + } )); cat.systems.push_back(FurnaceGUISysDef( "PC + AdLib/Sound Blaster (drums mode)", { DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16), 0 } )); @@ -1165,6 +1215,7 @@ void FurnaceGUI::initSystemPresets() { DIV_SYSTEM_OPL2, 64, 0, 0, DIV_SYSTEM_SAA1099, 64, 0, 1, DIV_SYSTEM_SAA1099, 64, 0, 1, + DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16), DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } @@ -1174,6 +1225,7 @@ void FurnaceGUI::initSystemPresets() { DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, DIV_SYSTEM_SAA1099, 64, 0, 1, DIV_SYSTEM_SAA1099, 64, 0, 1, + DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16), DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } @@ -1182,6 +1234,7 @@ void FurnaceGUI::initSystemPresets() { "PC + Sound Blaster Pro", { DIV_SYSTEM_OPL2, 64, -127, 0, DIV_SYSTEM_OPL2, 64, 127, 0, + DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16)|(1<<20), //alternatively 44.1 khz mono DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } @@ -1190,6 +1243,7 @@ void FurnaceGUI::initSystemPresets() { "PC + Sound Blaster Pro (drums mode)", { DIV_SYSTEM_OPL2_DRUMS, 64, -127, 0, DIV_SYSTEM_OPL2_DRUMS, 64, 127, 0, + DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16)|(1<<20), //alternatively 44.1 khz mono DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } @@ -1197,6 +1251,7 @@ void FurnaceGUI::initSystemPresets() { cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro 2", { DIV_SYSTEM_OPL3, 64, 0, 0, + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20), DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } @@ -1204,6 +1259,7 @@ void FurnaceGUI::initSystemPresets() { cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro 2 (drums mode)", { DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20), DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index ba3ded1a6..3a0c1098e 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -217,7 +217,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) { copyOfFlags=(flags&(~15))|1; } - if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) { + if (ImGui::RadioButton("Arcade (4MHz)",(flags&15)==2)) { copyOfFlags=(flags&(~15))|2; } if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) { From eb0aac0f54f3cfae1a847edc65de463d5629d394 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 20 Sep 2022 19:07:17 -0500 Subject: [PATCH 33/63] GUI: more work on it --- src/gui/gui.h | 4 +- src/gui/pattern.cpp | 100 +++++++++++++++++++++++++++++-------------- src/gui/settings.cpp | 10 ++++- 3 files changed, 79 insertions(+), 35 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index bbfb3cf5b..5bf8adfe7 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1179,6 +1179,7 @@ class FurnaceGUI { int channelVolStyle; int channelFeedbackStyle; int channelFont; + int channelTextCenter; int maxRecentFile; unsigned int maxUndoSteps; String mainFontPath; @@ -1297,10 +1298,11 @@ class FurnaceGUI { saveUnusedPatterns(0), channelColors(1), channelTextColors(0), - channelStyle(0), + channelStyle(1), channelVolStyle(0), channelFeedbackStyle(1), channelFont(1), + channelTextCenter(1), maxRecentFile(10), maxUndoSteps(100), mainFontPath(""), diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index add285f64..103ecbbe2 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -18,6 +18,7 @@ */ // for suck's fake Clang extension! +#include #define _USE_MATH_DEFINES #include "gui.h" #include "../ta-log.h" @@ -25,6 +26,7 @@ #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" #include "guiConst.h" +#include "../utfutils.h" #include inline float randRange(float min, float max) { @@ -448,27 +450,6 @@ void FurnaceGUI::drawPattern() { if (!e->curSubSong->chanShow[i]) continue; ImGui::TableNextColumn(); bool displayTooltip=false; - if (e->curSubSong->chanCollapse[i]) { - const char* chName=e->getChannelShortName(i); - if (strlen(chName)>3) { - snprintf(chanID,2048,"...##_CH%d",i); - } else { - snprintf(chanID,2048,"%s##_CH%d",chName,i); - } - displayTooltip=true; - } else { - const char* chName=e->getChannelName(i); - size_t chNameLimit=6+4*e->curPat[i].effectCols; - if (strlen(chName)>chNameLimit) { - String shortChName=chName; - shortChName.resize(chNameLimit-3); - shortChName+="..."; - snprintf(chanID,2048," %s##_CH%d",shortChName.c_str(),i); - displayTooltip=true; - } else { - snprintf(chanID,2048," %s##_CH%d",chName,i); - } - } bool muted=e->isChannelMuted(i); ImVec4 chanHead=muted?uiColors[GUI_COLOR_CHANNEL_MUTED]:channelColor(i); @@ -532,10 +513,14 @@ void FurnaceGUI::drawPattern() { ImGuiWindow* window=ImGui::GetCurrentWindow(); ImVec2 size=ImVec2( 1.0f, - lineHeight+1.0f*dpiScale+6.0*dpiScale + lineHeight+1.0f*dpiScale ); ImDrawList* dl=ImGui::GetWindowDrawList(); + if (settings.channelStyle!=0) { + size.y+=6.0f*dpiScale; + } + if (settings.channelStyle==2) { size.y+=6.0f*dpiScale; } @@ -546,13 +531,63 @@ void FurnaceGUI::drawPattern() { minArea.y+size.y ); ImRect rect=ImRect(minArea,maxArea); + float padding=ImGui::CalcTextSize("A").x; + + ImVec2 minLabelArea=minArea; + ImVec2 maxLabelArea=maxArea; + + if (e->curSubSong->chanCollapse[i]) { + const char* chName=e->getChannelShortName(i); + if (strlen(chName)>3) { + snprintf(chanID,2048,"..."); + } else { + snprintf(chanID,2048,"%s",chName); + } + displayTooltip=true; + } else { + minLabelArea.x+=padding; + maxLabelArea.x-=padding; + const char* chName=e->getChannelName(i); + float chNameLimit=maxLabelArea.x-minLabelArea.x; + if (ImGui::CalcTextSize(chName).x>chNameLimit) { + String shortChName; + float totalAdvanced=0.0f; + float ellipsisSize=ImGui::CalcTextSize("...").x; + for (const char* j=chName; *j;) { + signed char l; + int ch=decodeUTF8((const unsigned char*)j,l); + + totalAdvanced+=ImGui::GetFont()->GetCharAdvance(ch); + if (totalAdvanced>(chNameLimit-ellipsisSize)) break; + + for (int k=0; kAddRectFilled(rect.Min,rect.Max,col); + dl->AddText(ImVec2(minLabelArea.x,rect.Min.y),ImGui::GetColorU32(channelTextColor(i)),chanID); } - ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale)); break; case 1: { // line ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); @@ -572,7 +607,7 @@ void FurnaceGUI::drawPattern() { )); dl->AddRectFilledMultiColor(rect.Min,rect.Max,fadeCol0,fadeCol0,fadeCol,fadeCol); dl->AddLine(ImVec2(rect.Min.x,rect.Max.y),ImVec2(rect.Max.x,rect.Max.y),ImGui::GetColorU32(chanHeadBase),2.0f*dpiScale); - dl->AddText(ImVec2(rect.Min.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID,strstr(chanID,"##")); + dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; } @@ -599,18 +634,16 @@ void FurnaceGUI::drawPattern() { rMax.x-=3.0f*dpiScale; rMax.y-=6.0f*dpiScale; dl->AddRectFilledMultiColor(rMin,rMax,fadeCol0,fadeCol0,fadeCol,fadeCol,4.0f*dpiScale); - dl->AddText(ImVec2(rect.Min.x,rect.Min.y+6.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID,strstr(chanID,"##")); + dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+6.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; } case 3: // split button ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale)); - ImGui::TextUnformatted(chanID,strstr(chanID,"##")); + ImGui::TextUnformatted(chanID); ImGui::SameLine(); ImGui::PushFont(mainFont); - ImGui::PushID(chanID); ImGui::SmallButton(muted?ICON_FA_VOLUME_OFF:ICON_FA_VOLUME_UP); - ImGui::PopID(); ImGui::PopFont(); break; case 4: { // square border @@ -630,7 +663,7 @@ void FurnaceGUI::drawPattern() { rMax.x-=3.0f*dpiScale; rMax.y-=3.0f*dpiScale; dl->AddRect(rMin,rMax,fadeCol,0.0f,2.0*dpiScale); - dl->AddText(ImVec2(rect.Min.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID,strstr(chanID,"##")); + dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; } @@ -651,11 +684,12 @@ void FurnaceGUI::drawPattern() { rMax.x-=3.0f*dpiScale; rMax.y-=3.0f*dpiScale; dl->AddRect(rMin,rMax,fadeCol,4.0f*dpiScale,ImDrawFlags_RoundCornersAll,2.0*dpiScale); - dl->AddText(ImVec2(rect.Min.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID,strstr(chanID,"##")); + dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID); } break; } } + ImGui::PopID(); if (displayTooltip && ImGui::IsItemHovered()) { ImGui::SetTooltip("%s",e->getChannelName(i)); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index e5c609829..bf360daed 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1355,6 +1355,11 @@ void FurnaceGUI::drawSettings() { settings.channelFont=1; } + bool channelTextCenterB=settings.channelTextCenter; + if (ImGui::Checkbox("Center channel name",&channelTextCenterB)) { + settings.channelTextCenter=channelTextCenterB; + } + ImGui::Separator(); bool insEditColorizeB=settings.insEditColorize; @@ -2291,10 +2296,11 @@ void FurnaceGUI::syncSettings() { settings.saveUnusedPatterns=e->getConfInt("saveUnusedPatterns",0); settings.channelColors=e->getConfInt("channelColors",1); settings.channelTextColors=e->getConfInt("channelTextColors",0); - settings.channelStyle=e->getConfInt("channelStyle",0); + settings.channelStyle=e->getConfInt("channelStyle",1); settings.channelVolStyle=e->getConfInt("channelVolStyle",0); settings.channelFeedbackStyle=e->getConfInt("channelFeedbackStyle",1); settings.channelFont=e->getConfInt("channelFont",1); + settings.channelTextCenter=e->getConfInt("channelTextCenter",1); settings.maxRecentFile=e->getConfInt("maxRecentFile",10); clampSetting(settings.mainFontSize,2,96); @@ -2395,6 +2401,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.channelVolStyle,0,4); clampSetting(settings.channelFeedbackStyle,0,3); clampSetting(settings.channelFont,0,1); + clampSetting(settings.channelTextCenter,0,1); clampSetting(settings.maxRecentFile,0,30); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); @@ -2551,6 +2558,7 @@ void FurnaceGUI::commitSettings() { e->setConf("channelVolStyle",settings.channelVolStyle); e->setConf("channelFeedbackStyle",settings.channelFeedbackStyle); e->setConf("channelFont",settings.channelFont); + e->setConf("channelTextCenter",settings.channelTextCenter); e->setConf("maxRecentFile",settings.maxRecentFile); // colors From 28d34171968dbdeff4b3221248fcff3e5a4bf6e6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 20 Sep 2022 19:41:07 -0500 Subject: [PATCH 34/63] GUI: more channel bar polishing --- src/gui/pattern.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 103ecbbe2..ae194925d 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -547,6 +547,9 @@ void FurnaceGUI::drawPattern() { } else { minLabelArea.x+=padding; maxLabelArea.x-=padding; + if (settings.channelStyle==3) { // split button + maxLabelArea.x-=ImGui::GetFrameHeightWithSpacing(); + } const char* chName=e->getChannelName(i); float chNameLimit=maxLabelArea.x-minLabelArea.x; if (ImGui::CalcTextSize(chName).x>chNameLimit) { @@ -578,6 +581,8 @@ void FurnaceGUI::drawPattern() { minLabelArea.x+=0.5f*(maxLabelArea.x-minLabelArea.x-ImGui::CalcTextSize(chanID).x); } + if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0.0f,0.0f)); + ImGui::PushID(2048+i); switch (settings.channelStyle) { case 0: // classic @@ -640,6 +645,7 @@ void FurnaceGUI::drawPattern() { } case 3: // split button ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale)); + ImGui::SetCursorPosX(minLabelArea.x); ImGui::TextUnformatted(chanID); ImGui::SameLine(); ImGui::PushFont(mainFont); @@ -691,6 +697,8 @@ void FurnaceGUI::drawPattern() { } ImGui::PopID(); + if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PopStyleVar(); + if (displayTooltip && ImGui::IsItemHovered()) { ImGui::SetTooltip("%s",e->getChannelName(i)); } @@ -739,20 +747,24 @@ void FurnaceGUI::drawPattern() { } if (e->isRunning()) { + DivChannelState* cs=e->getChanState(i); + float stereoPan=(float)(e->convertPanSplitToLinearLR(cs->panL,cs->panR,256)-128)/128.0; switch (settings.channelVolStyle) { case 1: // simple xRight=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.9+(keyHit1[i]*0.1f); break; - case 2: // stereo - xRight=0.5+((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.4+(keyHit1[i]*0.1f); - xLeft=1.0-xRight; + case 2: { // stereo + float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.4+(keyHit1[i]*0.1f); + xRight=0.5+amount*(1.0+MIN(0.0,stereoPan)); + xLeft=0.5-amount*(1.0-MAX(0.0,stereoPan)); break; + } case 3: // real xRight=chanOscVol[i]; break; case 4: // real (stereo) - xRight=0.5+chanOscVol[i]*0.5; - xLeft=1.0-xRight; + xRight=0.5+chanOscVol[i]*0.5*(1.0+MIN(0.0,stereoPan)); + xLeft=0.5-chanOscVol[i]*0.5*(1.0-MAX(0.0,stereoPan)); break; } From 24a40051352b5ef7b2953a8e0dc7018a65f8369f Mon Sep 17 00:00:00 2001 From: TheDuccinator <66538032+TheDuccinator@users.noreply.github.com> Date: Tue, 20 Sep 2022 18:53:48 -0700 Subject: [PATCH 35/63] Add new GB song "You're Doing Well!" --- demos/You're_Doing_Well!_GB.fur | Bin 0 -> 3855 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/You're_Doing_Well!_GB.fur diff --git a/demos/You're_Doing_Well!_GB.fur b/demos/You're_Doing_Well!_GB.fur new file mode 100644 index 0000000000000000000000000000000000000000..01cfff9d2cb81f9a3e8b9fe5f9a62e72bd2e5ff7 GIT binary patch literal 3855 zcmZvdc{J4R|Hkc$t2e1#%2*=PY(;rtDPj7X;8dz?$w|}R?rt&Y{xI*w z&U?~|sIQ&(?z=_#PrcNCqz6SGGd`G3Ym07aMvNjn3z~<0Jf;^2<|{wx7O1tUNJcDv zd0{$&cLUWJjhtmXuE)km#{LjR%MY>#@&@IJK_5=zwS@|8qPLkoPwdGqvax_w$gcDy zDrwudve^|B%Ii&0u%x8mOQ1kUqNPP=_Q$;r2W&r%suZS*ac>0fz-w)Fufyz&VTGAb zH|P@W)1IRIFB0f{s`#5&m3AXWR#JYe)wKo(SgoKXw`mbD}Nk zSnaL%QcCl(JGf&|3jZg!vp)%SC}ZEsKO;%RhB|AegF_mycmSNbfqG*-kx&hN^B(2Z zBDoL6C$W7+%_!DgMqb~V-CPw%StiU;FRqgNdVg zVj9vK)Ob(_2VkZ@e3GjA*rr;mUQSe94%7cQ+osKO%wo^fa^6m*Jf}6g+$Z0CE&us+ zo(W2`bzpB(vk@=qUPVeD_2D)n%`4XR@?hioi3Z21{8F3JmcMpROB)Dib=cVqAzqMW7nQX`!bU( z%f@SzHnl$VHhT9L)hf&>e)t}lt*@Suc&H|k5T z?+(bm7JaDcpMp06LRnMKb_)x)HE(K!UDRI(?vDibmk)3bN0_wf9laoLvP;V(IwRiP zFiydl?v#*j3c0rABo{$A8*=uQDcfN1*^=h4$wA9!?t&1VKDm+*Iwdo?hKecqv6-B5 zx6cpG+FP2g26}s@Mf<= zEKil`XF+{Yu$#)+s!o;2`pj!-PiwhSLFuIodASi`$GO=oOlf0b_6;|+E9e(J3aC0t zXkPspXmOd__}kL&ftD9z_BaM(pX}61G{KarOUaFhIvOZs)Y}miPZu7Su4X*gY3-Jm z)i{5!XMg9-lIcG}^9;{8m(=(>4wGYQejKV9Q8Si(x|-6_5wC$CE7id78pdu3&&66^ ziTd>U>F%*DU&k+m)_QIEx>xa=uaSv;`^7IR77kR%*VIej*$hs8^{5OV+81T~n-xke zSJ&~_U?A;AJRO(TGpo1H8j-XS#ciep#D(d@d?QEjDJQkmOuOw!T1^OR(DGB= zwPy5@#~(4;9`MNzHC~xOn2fJd{4cY9){rq6j!dvzvd?way;*#J=gk*Svh9NS zLA*g8B;!y!_o`w<9VO;hM7_U7G#-o{+ZnL1nFX_^6k7!)m5!Bb12;=1f5mb9w>UCJ z)L;ir@%%ayO22b7D`EtOJhngl@fMQuZ%E;n5^_u1mq?1UwJx_F-F!OhejG{5&>4(3 zzjjZ!Y;{5qKmg|iF_nW* z&$NTN8+h4Uzk~|E3w33saRt|pEtM-4)w+y2dui#`mGIm?WCr=<5(d{_vjp$WsV*IX zc-bvVyMKjU5U+&AQrnQv<2MT~^M}5^^QiZDU@(~d^4}6uKTp_~9>^M5NZe^CmGe9+ zH2X?ll=g4^J+I|L@^TpWGqG^y5f2tgJ;a4)o>+V?QNEIZpretvA`z2S!Asu0mG9Lr zD=p(NNl5v-+vj&>RAohFm*Ta3M0t$9Hs{aL88e1U@s4Jvn%zEIYE5e`75Hyfxa4Iy z+ngdqz;`WXn6|Lz zW6Q1uYz1fq#sT@Y&NS*eH_E<<_;UFJ)e6?=x9L-MYf*i==G&ua4dBSju}Lwz`?^?#1-_qD; z(s~N^-s;E#$L{)9Qa>^!p8z)9tJ3ZK2*D5V&gS`)ucLYp_!KDH1tg>j7|L9#!4yj8 z58!e)Qttw+j15EGpgvrb->~0w`POLGpEH200V)Im0@8kN<|=~z4qOo+$mF;U1nJ>L z!MV+)4Ra%~oaMBJx(uxC=MD-Q<`=2CV~921e<%#4USjYb5a_mx9)oUkC-{Ha zYN)NYevrArFr5O+NW>+2C5v1c&o5UISw%|CK-{Opc*(u=>kUdzMp}H#H9V#+C-C#b zq=p_A551G%PUR~Mu};hkeF=DPc?c|fz&GpB??nRncHe_=Bvcy-ck7@RBmd)V{%FFl z4BJ{<6Yc%Pk3_p3h)xNHUez3u#x(2?wLBW;JP{V&YTZ@cczwh2F#l#={#@)n{v!Y zsg<$s{{tgWNCfcL%}nuLuo!LXUtO(02p_Xgw!<)+G?NN1c8~{es0o_pDdZhr0=@0j zR{3YVvB)spDu-q4GKIWNd(&TgkwO;N-rT3aSzJJ82;M4iqVBUIwkDmm1flsW(gyeU zk~DkEPdvhE^g6%;^WZj#SMOjHX_1fi;M9z5RJzx1ck`DNXta3@R(eDWOh~8N0WFJ> zpGsD}jIA%Bys=#Pf8Jv`a!IELN@%zr*i!=~B(rgKy<=qKt9N)!v=pJ|J?M7y8!mdl zegmuXB!xrLDegz{2Vy7g#<&9I&WnwOmqYAmk~6gUC_v{I?mxcFukp>@>y1scOo{h{ zl4F_d!``kX@$P$z(TZHN1Mrq2=+Mi;b^frG-0_u2Bya;96oRR#(^2f(+H=34bmVg7 z*l=;kD`nA6-ugK*P$jQ1st6e&;Wp5RKp6wV6P<#jGn@I(;1SlW!zO%*r@RFOAS*6% zH|DYMmBigzHumooaXkgFGq+k~%eopd#K2t#a1Cc|wkA;UikQk{otR^G{^^~*{Qvb3 z*wJ^~E(#|%>l|s-JAxHaDXX^o)Wv{o_|zaDeuMkKALEqOF{mcM$O0Hi7Ch(XD1#?R zng6*vM>`zp)+D8oE^c{B-F5=4Z_z%q9?tJuMNwD_;z7s);pg50Cs8qhY$m~#l5x=R zgSyeWJ3hbq)$5tRMdD;qEwTY`ITwz-AyQ}kqy>k1a2gf+>hL(qY`#3_13WmE!ZNBN zZetdXc&dW6@c`ceT~w{XyqjB%(>Y#1!<`Sbz2L&{`-FPhh04#Bzd*Eq zAa4!)6#0r>^)lz2+t{J8i2w9%n}^`8?oR|k*t-zU5w5o9JMTZP2rX^y!L8s{)JLHO zpcI6YbMi`BIJ3smy}{zk&I(s+P-r*aUeuqUyh>MIZ;!6>JWm>h4CS0W&+NX66sr?O zRU_g$3jLJgtE;uJarvRim|UK%${T z^dn$4i5zBo7)g2bnI-ba!RwRNmypv7@CnG>*u1~A#Tq*`cCw zULPkV*s2Gp#!q5PTm3Z{r{hL#33_ua670gPwe5W>4J6`n2Km=7?5HA=nDf>EO7Sn+ zopl~&D>e7f!o99_2F8UMp{DtPacZb%Muub>afZEY0~}t1EXpRydNNb}LKQXlg7dsB WQ#4=iLekbwFCeJ{^0dFsE&LxqZU*oG literal 0 HcmV?d00001 From e1679e8ea68e6dd9dc56e414e1de77d0c56b824e Mon Sep 17 00:00:00 2001 From: cam900 Date: Wed, 21 Sep 2022 13:22:04 +0900 Subject: [PATCH 36/63] Fix preset Atari ST series uses YM2419 or YM3439 --- src/gui/presets.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index da1c4d51f..7d83dc3fb 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -1114,13 +1114,13 @@ void FurnaceGUI::initSystemPresets() { )); cat.systems.push_back(FurnaceGUISysDef( "Atari ST", { - DIV_SYSTEM_AY8910, 64, 0, 3, + DIV_SYSTEM_AY8910, 64, 0, 19, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "Atari STE", { - DIV_SYSTEM_AY8910, 64, 0, 3, + DIV_SYSTEM_AY8910, 64, 0, 19, DIV_SYSTEM_PCM_DAC, 64, 0, 50667|(7<<16), DIV_SYSTEM_PCM_DAC, 64, 0, 50667|(7<<16), 0 From 0569af91037c894bc792238c587e38c93ba1ec52 Mon Sep 17 00:00:00 2001 From: nicco1690 <78063037+nicco1690@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:18:12 -0400 Subject: [PATCH 37/63] DMF demo song submission reminder --- demos/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/demos/README.md b/demos/README.md index ea3ef883e..43f29d793 100644 --- a/demos/README.md +++ b/demos/README.md @@ -11,4 +11,6 @@ contact me or send a pull request if you want your song to be added to this coll - Nintendo covers are frowned upon - big label music covers also are discouraged +tildearrow also accepts demo songs in the .DMF format as well as the .FUR format. + thank you for contributing! From 60abdd78a1c019b3d453366318fdd1bcd68788cd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 21 Sep 2022 16:45:05 -0500 Subject: [PATCH 38/63] and yet another big endian fix --- src/engine/fileOps.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 10a4bcce9..41fc767af 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1772,14 +1772,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { #ifdef TA_BIG_ENDIAN // convert 16-bit samples to big-endian - if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) { - unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf(); - size_t sampleBufLen=sample->getCurBufLen(); - for (size_t pos=0; pos>8)|((unsigned short)data[pos]<<8); } #endif From a17f499384201e1674e556e8576d9841aa78c56d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 21 Sep 2022 16:52:04 -0500 Subject: [PATCH 39/63] ... --- src/engine/fileOps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 41fc767af..aac771e09 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1772,7 +1772,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { #ifdef TA_BIG_ENDIAN // convert 16-bit samples to big-endian - for (size_t pos=0; pos>8)|((unsigned short)data[pos]<<8); } #endif From e22d7484cb3d4344c404c58e9762a72fc5b6a5b4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 21 Sep 2022 19:27:42 -0500 Subject: [PATCH 40/63] dev115 - automatic system detection --- papers/format.md | 11 ++++- src/engine/engine.h | 4 +- src/engine/fileOps.cpp | 14 ++++++- src/engine/instrument.cpp | 12 +++++- src/engine/instrument.h | 4 +- src/engine/song.h | 4 +- src/gui/gui.cpp | 87 +++++++++++++++++++++++++++++++++++++++ src/gui/gui.h | 3 +- src/gui/songInfo.cpp | 14 ++++++- src/gui/sysConf.cpp | 3 ++ src/gui/sysManager.cpp | 6 +++ 11 files changed, 150 insertions(+), 12 deletions(-) diff --git a/papers/format.md b/papers/format.md index da06488fa..acedd9945 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 115: Furnace dev115 - 114: Furnace dev114 - 113: Furnace dev113 - 112: Furnace dev112 @@ -342,7 +343,9 @@ size | description 1 | SN periods under 8 are treated as 1 (>=108) or reserved 1 | cut/delay effect policy (>=110) or reserved 1 | 0B/0D effect treatment (>=113) or reserved - 4 | reserved + 1 | automatic system name detection (>=115) or reserved + | - this one isn't a compatibility flag, but it's here for convenience... + 3 | reserved --- | **virtual tempo data** 2 | virtual tempo numerator of first song (>=96) or reserved 2 | virtual tempo denominator of first song (>=96) or reserved @@ -499,7 +502,11 @@ size | description 1 | ws 1 | ksr 1 | operator enabled (>=114) or reserved - 11 | reserved + 1 | KVS mode (>=115) or reserved + | - 0: off + | - 1: on + | - 2: auto (depending on alg) + 10 | reserved --- | **Game Boy instrument data** 1 | volume 1 | direction diff --git a/src/engine/engine.h b/src/engine/engine.h index 3047a8af7..40bbbbb90 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -46,8 +46,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev114" -#define DIV_ENGINE_VERSION 114 +#define DIV_VERSION "dev115" +#define DIV_ENGINE_VERSION 115 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index aac771e09..4c213744b 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1085,6 +1085,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<113) { ds.jumpTreatment=1; } + if (ds.version<115) { + ds.autoSystem=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -1512,7 +1515,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<4; i++) { + if (ds.version>=115) { + ds.autoSystem=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<3; i++) { reader.readC(); } } @@ -1549,6 +1557,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.categoryJ=reader.readString(); } else { ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0)); + ds.autoSystem=true; } // read subsongs @@ -3751,7 +3760,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.snNoLowPeriods); w->writeC(song.delayBehavior); w->writeC(song.jumpTreatment); - for (int i=0; i<4; i++) { + w->writeC(song.autoSystem); + for (int i=0; i<3; i++) { w->writeC(0); } diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 22cdddf4b..3296994ba 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -72,9 +72,10 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(op.ksr); w->writeC(op.enable); + w->writeC(op.kvs); // reserved - for (int k=0; k<11; k++) { + for (int k=0; k<10; k++) { w->writeC(0); } } @@ -724,8 +725,15 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { reader.readC(); } + if (version>=115) { + op.kvs=reader.readC(); + } else { + op.kvs=2; + reader.readC(); + } + // reserved - for (int k=0; k<11; k++) reader.readC(); + for (int k=0; k<10; k++) reader.readC(); } // GB diff --git a/src/engine/instrument.h b/src/engine/instrument.h index e5372933d..5652301f4 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -87,6 +87,7 @@ struct DivInstrumentFM { bool enable; unsigned char am, ar, dr, mult, rr, sl, tl, dt2, rs, dt, d2r, ssgEnv; unsigned char dam, dvb, egt, ksl, sus, vib, ws, ksr; // YMU759/OPL/OPZ + unsigned char kvs; Operator(): enable(true), am(0), @@ -108,7 +109,8 @@ struct DivInstrumentFM { sus(0), vib(0), ws(0), - ksr(0) {} + ksr(0), + kvs(2) {} } op[4]; DivInstrumentFM(): alg(0), diff --git a/src/engine/song.h b/src/engine/song.h index 493fc4125..09a89378c 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -511,6 +511,7 @@ struct DivSong { bool e1e2StopOnSameNote; bool brokenPortaArp; bool snNoLowPeriods; + bool autoSystem; std::vector ins; std::vector wave; @@ -614,7 +615,8 @@ struct DivSong { brokenOutVol(false), e1e2StopOnSameNote(false), brokenPortaArp(false), - snNoLowPeriods(false) { + snNoLowPeriods(false), + autoSystem(true) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a2eb9c920..2e566bb1a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -584,6 +584,78 @@ void FurnaceGUI::updateWindowTitle() { if (sdlWin!=NULL) SDL_SetWindowTitle(sdlWin,title.c_str()); } +void FurnaceGUI::autoDetectSystem() { + std::map sysCountMap; + for (int i=0; isong.systemLen; i++) { + try { + sysCountMap.at(e->song.system[i])++; + } catch (std::exception& ex) { + sysCountMap[e->song.system[i]]=1; + } + } + + logV("sysCountMap:"); + for (std::pair k: sysCountMap) { + logV("%s: %d",e->getSystemName(k.first),k.second); + } + + bool isMatch=false; + std::map defCountMap; + for (FurnaceGUISysCategory& i: sysCategories) { + for (FurnaceGUISysDef& j: i.systems) { + defCountMap.clear(); + for (size_t k=0; k k: defCountMap) { + logV("- %s: %d",e->getSystemName(k.first),k.second); + } + for (std::pair k: defCountMap) { + try { + if (sysCountMap.at(k.first)!=k.second) { + isMatch=false; + break; + } + } catch (std::exception& ex) { + isMatch=false; + break; + } + } + if (isMatch) { + logV("match found!"); + e->song.systemName=j.name; + break; + } + } + if (isMatch) break; + } + + if (!isMatch) { + bool isFirst=true; + e->song.systemName=""; + for (std::pair k: sysCountMap) { + if (!isFirst) e->song.systemName+=" + "; + if (k.second>1) { + e->song.systemName+=fmt::sprintf("%d×",k.second); + } + if (k.first==DIV_SYSTEM_N163) { + e->song.systemName+=settings.c163Name; + } else { + e->song.systemName+=e->getSystemName(k.first); + } + isFirst=false; + } + } +} + ImVec4 FurnaceGUI::channelColor(int ch) { switch (settings.channelColors) { case 0: @@ -3262,6 +3334,9 @@ bool FurnaceGUI::loop() { showError("cannot add chip! ("+e->getLastError()+")"); } ImGui::CloseCurrentPopup(); + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); } ImGui::EndMenu(); @@ -3282,6 +3357,9 @@ bool FurnaceGUI::loop() { DivSystem picked=systemPicker(); if (picked!=DIV_SYSTEM_NULL) { e->changeSystem(i,picked,preserveChanPos); + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); ImGui::CloseCurrentPopup(); } @@ -3297,6 +3375,10 @@ bool FurnaceGUI::loop() { if (!e->removeSystem(i,preserveChanPos)) { showError("cannot remove chip! ("+e->getLastError()+")"); } + if (e->song.autoSystem) { + autoDetectSystem(); + updateWindowTitle(); + } } } ImGui::EndMenu(); @@ -4415,6 +4497,10 @@ bool FurnaceGUI::loop() { case GUI_WARN_SYSTEM_DEL: if (ImGui::Button("Yes")) { e->removeSystem(sysToDelete,preserveChanPos); + if (e->song.autoSystem) { + autoDetectSystem(); + updateWindowTitle(); + } ImGui::CloseCurrentPopup(); } ImGui::SameLine(); @@ -5069,6 +5155,7 @@ FurnaceGUI::FurnaceGUI(): macroPointSize(16), waveEditStyle(0), mobileMenuPos(0.0f), + autoButtonSize(0.0f), curSysSection(NULL), pendingRawSampleDepth(8), pendingRawSampleChannels(1), diff --git a/src/gui/gui.h b/src/gui/gui.h index 5bf8adfe7..c44d4c122 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1018,7 +1018,7 @@ class FurnaceGUI { int drawHalt; int macroPointSize; int waveEditStyle; - float mobileMenuPos; + float mobileMenuPos, autoButtonSize; const int* curSysSection; String pendingRawSample; @@ -1610,6 +1610,7 @@ class FurnaceGUI { bool CWVSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format="%d", ImGuiSliderFlags flags=0); void updateWindowTitle(); + void autoDetectSystem(); void prepareLayout(); ImVec4 channelColor(int ch); ImVec4 channelTextColor(int ch); diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index 44982abaf..5b113c641 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -77,11 +77,23 @@ void FurnaceGUI::drawSongInfo() { ImGui::TableNextColumn(); ImGui::Text("System"); ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); + ImGui::SetNextItemWidth(MAX(16.0f*dpiScale,avail-autoButtonSize-ImGui::GetStyle().ItemSpacing.x)); if (ImGui::InputText("##SystemName",&e->song.systemName)) { MARK_MODIFIED; updateWindowTitle(); + e->song.autoSystem=false; } + ImGui::SameLine(); + pushToggleColors(e->song.autoSystem); + if (ImGui::Button("Auto")) { + e->song.autoSystem=!e->song.autoSystem; + if (e->song.autoSystem) { + autoDetectSystem(); + updateWindowTitle(); + } + } + popToggleColors(); + autoButtonSize=ImGui::GetItemRectSize().x; ImGui::EndTable(); } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 3a0c1098e..900845a69 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -765,6 +765,9 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool if (copyOfFlags!=flags) { if (chan>=0) { e->setSysFlags(chan,copyOfFlags,restart); + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); } else { flags=copyOfFlags; diff --git a/src/gui/sysManager.cpp b/src/gui/sysManager.cpp index 98f939d0f..386d26583 100644 --- a/src/gui/sysManager.cpp +++ b/src/gui/sysManager.cpp @@ -82,6 +82,9 @@ void FurnaceGUI::drawSysManager() { DivSystem picked=systemPicker(); if (picked!=DIV_SYSTEM_NULL) { e->changeSystem(i,picked,preserveChanPos); + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); ImGui::CloseCurrentPopup(); } @@ -110,6 +113,9 @@ void FurnaceGUI::drawSysManager() { if (!e->addSystem(picked)) { showError("cannot add chip! ("+e->getLastError()+")"); } + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); ImGui::CloseCurrentPopup(); } From ad097e0526b15fa73c56cfb61aea10cfcf332f34 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 21 Sep 2022 22:59:53 -0500 Subject: [PATCH 41/63] GUI: add paste with instrument like paste mix but changes the instrument --- TODO.md | 3 +-- src/gui/editing.cpp | 15 +++++++++------ src/gui/gui.cpp | 24 ++++++++++++++++++++++++ src/gui/gui.h | 6 ++++-- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/TODO.md b/TODO.md index 028f21077..84bc08a73 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ # to-do for 0.6pre1.5 - stereo separation control for AY -- "paste with instrument" -- auto-detect system +- KVS - bug fixes diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 201c9be26..153d70da3 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -405,7 +405,7 @@ void FurnaceGUI::doCopy(bool cut) { } } -void FurnaceGUI::doPaste(PasteMode mode) { +void FurnaceGUI::doPaste(PasteMode mode, int arg) { finishSelection(); prepareUndo(GUI_UNDO_PATTERN_PASTE); char* clipText=SDL_GetClipboardText(); @@ -481,14 +481,16 @@ void FurnaceGUI::doPaste(PasteMode mode) { continue; } - if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG) && strcmp(note,"...")==0) { + if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG || + mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) && strcmp(note,"...")==0) { // do nothing. } else { - if (mode!=GUI_PASTE_MODE_MIX_BG || (pat->data[j][0]==0 && pat->data[j][1]==0)) { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->data[j][0]==0 && pat->data[j][1]==0)) { if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) { invalidData=true; break; } + if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->data[j][2]=arg; } } } else { @@ -505,7 +507,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { note[2]=0; if (iFine==1) { - if (!opMaskPaste.ins) { + if (!opMaskPaste.ins || mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) { iFine++; continue; } @@ -527,7 +529,8 @@ void FurnaceGUI::doPaste(PasteMode mode) { } if (strcmp(note,"..")==0) { - if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG)) { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG || + mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) { pat->data[j][iFine+1]=-1; } } else { @@ -536,7 +539,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { invalidData=true; break; } - if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) { if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val; } } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 2e566bb1a..c374576ef 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2180,6 +2180,30 @@ void FurnaceGUI::editOptions(bool topMenu) { if (ImGui::BeginMenu("paste special...")) { if (ImGui::MenuItem("paste mix",BIND_FOR(GUI_ACTION_PAT_PASTE_MIX))) doPaste(GUI_PASTE_MODE_MIX_FG); if (ImGui::MenuItem("paste mix (background)",BIND_FOR(GUI_ACTION_PAT_PASTE_MIX_BG))) doPaste(GUI_PASTE_MODE_MIX_BG); + if (ImGui::BeginMenu("paste with ins (foreground)")) { + if (e->song.ins.empty()) { + ImGui::Text("no instruments available"); + } + for (size_t i=0; isong.ins.size(); i++) { + snprintf(id,4095,"%.2X: %s",(int)i,e->song.ins[i]->name.c_str()); + if (ImGui::MenuItem(id)) { + doPaste(GUI_PASTE_MODE_INS_FG,i); + } + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("paste with ins (background)")) { + if (e->song.ins.empty()) { + ImGui::Text("no instruments available"); + } + for (size_t i=0; isong.ins.size(); i++) { + snprintf(id,4095,"%.2X: %s",(int)i,e->song.ins[i]->name.c_str()); + if (ImGui::MenuItem(id)) { + doPaste(GUI_PASTE_MODE_INS_BG,i); + } + } + ImGui::EndMenu(); + } if (ImGui::MenuItem("paste flood",BIND_FOR(GUI_ACTION_PAT_PASTE_FLOOD))) doPaste(GUI_PASTE_MODE_FLOOD); if (ImGui::MenuItem("paste overflow",BIND_FOR(GUI_ACTION_PAT_PASTE_OVERFLOW))) doPaste(GUI_PASTE_MODE_OVERFLOW); ImGui::EndMenu(); diff --git a/src/gui/gui.h b/src/gui/gui.h index c44d4c122..77a127595 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -596,7 +596,9 @@ enum PasteMode { GUI_PASTE_MODE_MIX_FG, GUI_PASTE_MODE_MIX_BG, GUI_PASTE_MODE_FLOOD, - GUI_PASTE_MODE_OVERFLOW + GUI_PASTE_MODE_OVERFLOW, + GUI_PASTE_MODE_INS_FG, + GUI_PASTE_MODE_INS_BG }; #define FURKMOD_CTRL (1U<<31) @@ -1708,7 +1710,7 @@ class FurnaceGUI { void doInsert(); void doTranspose(int amount, OperationMask& mask); void doCopy(bool cut); - void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL); + void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0); void doChangeIns(int ins); void doInterpolate(); void doFade(int p0, int p1, bool mode); From 51c142169fb73d12b07cc726fd1e8f34b37c57f3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 21 Sep 2022 23:25:57 -0500 Subject: [PATCH 42/63] GUI: OPZ compact editor fixes --- src/gui/insEdit.cpp | 96 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 50fd286b8..31dfdb357 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2928,6 +2928,14 @@ void FurnaceGUI::drawInsEdit() { } } + if (ins->type==DIV_INS_OPZ) { + ImGui::SameLine(); + bool fixedOn=op.egt; + if (ImGui::Checkbox("Fixed",&fixedOn)) { PARAMETER + op.egt=fixedOn; + } + } + //52.0 controls vert scaling; default 96 drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type); //P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable @@ -3012,23 +3020,85 @@ void FurnaceGUI::drawInsEdit() { ImGui::Text("%s",FM_NAME(FM_KSL)); } - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_MULT)); - - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) { - int detune=(op.dt&7)-(settings.unsignedDetune?0:3); + if (ins->type==DIV_INS_OPZ) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (CWSliderInt("##DT",&detune,-3,4)) { PARAMETER - op.dt=detune+(settings.unsignedDetune?0:3); - } rightClickable + P(CWSliderScalar(FM_NAME(FM_EGSHIFT),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_DT)); + ImGui::Text("%s",FM_NAME(FM_EGSHIFT)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar(FM_NAME(FM_REV),ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_REV)); + } + + if (ins->type==DIV_INS_OPZ) { + if (op.egt) { + int block=op.dt; + int freqNum=(op.mult<<4)|(op.dvb&15); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderInt(FM_NAME(FM_MULT),&block,0,7)) { PARAMETER + if (block<0) block=0; + if (block>7) block=7; + op.dt=block; + } rightClickable + ImGui::TableNextColumn(); + ImGui::Text("Block"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderInt(FM_NAME(FM_FINE),&freqNum,0,255)) { PARAMETER + if (freqNum<0) freqNum=0; + if (freqNum>255) freqNum=255; + op.mult=freqNum>>4; + op.dvb=freqNum&15; + } rightClickable + ImGui::TableNextColumn(); + ImGui::Text("FreqNum"); + } else { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_MULT)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar(FM_NAME(FM_FINE),ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_FINE)); + } + } else { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_MULT)); + } + + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) { + if (!(ins->type==DIV_INS_OPZ && op.egt)) { + int detune=(op.dt&7)-(settings.unsignedDetune?0:3); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderInt("##DT",&detune,-3,4)) { PARAMETER + op.dt=detune+(settings.unsignedDetune?0:3); + } rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_DT)); + } ImGui::TableNextRow(); ImGui::TableNextColumn(); From 401581e892d964afadc7573de4bc440f48879474 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 21 Sep 2022 23:41:22 -0500 Subject: [PATCH 43/63] fix 116.5 --- src/engine/playback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 898bb2b05..f6baeb4dc 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -379,7 +379,7 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xed: // delay if (effectVal!=0) { - bool comparison=(song.delayBehavior==1)?(effectVal<=nextSpeed):(effectValtimeBase+1))); if (song.delayBehavior==2) comparison=true; if (comparison) { chan[i].rowDelay=effectVal+1; From 7bb7fa0c24e78c1608920179a2e68e679c6fcaa8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 21 Sep 2022 23:45:32 -0500 Subject: [PATCH 44/63] Moon --- demos/Moon.fur | Bin 0 -> 16907 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/Moon.fur diff --git a/demos/Moon.fur b/demos/Moon.fur new file mode 100644 index 0000000000000000000000000000000000000000..366de04523561f89d0767a4c274df2214e39079f GIT binary patch literal 16907 zcmb_?c|26>AOA3xsVsA=o9v^KHdIK~8B|D8A(bt+WT&iIXK3PfWr?DYAwm@0h^!OJ zT3JezWt5rh%ZzQtEa!J-ENQvF-ygs4>!oStIp=vk>+}A6-p@H#Q^xP}W5$m7*CU^t z{%k$oSKc{c^IGEd#tQ@dbq6*l^YghL71(~@H44K&S$ck2;6UBMnf2#j@(A|2BYzva{v0>9m0L)5Gfe(ArcmsOySHC^-@7Ni`k!fQ<`{{mCzFA9K^X)`*|$<& zpY0PRw5QVajD$<6{q`b~>fhG?adv%W4_P6*cebjsYAPwULC++(!b&eG*)M1QL3o3g zJ=Js6C&7nRI!hXx9>^`FPPM;|Iq2QkUQ2gLw7Ggc#jEf2mBYc)#q9>Z(-kw)SNr1W z9kau3TP)}Q?iu*JrQn4fx+QpvexDVwg1N;*)0i0H@V5lo@0Na_1u;W-z5np_Ew-Z( z6`j)mFkWvs#G*TGDG7*fh_~5B`8P4rHSkFT6F5hF>mlL~&<|-($1v~WHqshJ4lxS; zam7EA26o-r5Tf9@;6cB_ZK7~@nkX!yNgHsvN*UA-pmf$>r3lvtQWUeVQlh#8DM*K_ z?Jpwax+ex3eOhLT)Y{aPIhe@=?TpDg^gnVIn$7VJ6|M1!RXy>lm38rW^fftLNS>T6 zI$I7If|Kiu(E7Asl4><*t6BO9AK*zZxat|>5#ZV9r|C~LPt{~*r&bUxQY*;LCVe8x zMAP2zAU7kukfID*Cw=9M%|7Cu;~8R}Jyqd76WH(`CNVrE&{$242XN1iS0K3puGLRQXsY=Bkfg z>cT>J1yx?qpQ#~6Nu57H+#v1RfDU>rV-cJygQo}SeFG|Gj0lbm#;iPG zAN?t|Iixc-8(UT37_V4hIYa8zr&3xvr%Ah2=Sci=R7$rc?b)!mZN9&S?OgBs#O@wK zA_3Ww$ZtK?T&p-wijf*@UWXfLw$csA=Dy2S@{zSwa3Trd9aDop`keJ1e&) zdq#J-_WVR=eDtW#w6)r8%5$?9b{=rXKXAjE**_breG3PNp=NJO&bWUw~@gvrZ&^>vML6GjcG#> zNtD+ys-$w9ds9JG>+PZ4#-FC&W=SybXQgo z2jPzf{ro=~@YZs%lRbN5ZKcEw2C*n#oJ2#^aX}(cgU7SZn!A+nDby-x5+wy-P^p3L zh=Q^mu_e~)8<5)jW6{B`2w;|oV$F+&7U9Gj@3eRTW0kQxzgjtF9w;%U!2TTSlx;m1>Gs(k-n%o8I#^<-~+H zaALx1$L&Z30n$FV25tM)Gv6PSC-PPF*ukalfqcfFZz1^Gje*g_?GJwisE%jLcw)Ps7D0l(_{n%hZG`Z<6#eJ-6u9N z608c|$88w2^o+QkkW!c}D_nKjNXmzqpgDavpLcBPy2S?k`iD3 z{1x89sF+Y7t1so+xJKB$aah=`u~MonZk<$b95U!HBNo+3o;L09oLGP(#G$Vm8nH-s z#{lFPhXbBt8irqVQdO9l7l=ZY@PS7Mx-$5UGq%8}3I2U&)e z3c&kf&GDAc6o&%GW^vXt`N`l5mfAE`H`++~XG$&Jqq#%cz4;p&oUQ#nA(>@q=}wn+ zQ==ohJ%`HO{9g%oQ$FCHlV0Q9n@1mRom`_qxnZNvy5ORp|M}A`f@Iq*+E$Hc-Wkz7 z#O;!5v#+94e2(f1^%ZYT?EQDENvVszjrV+X#e}w`|9ogPW7=Pm)}*ISxn`t6>E5mW zY?v%r`ps1R+2lRR(zny<6uRO>!!?_}TM_uIt>~HX=!&T)lC(TOU*cq6=PmTY34IoG zK$13T;ztCkT5pvWcoYBj@FjLldJ{id_z>NZ;|)-B$1S6RiH7)V1~#ox#OIb<{5zu! zv_ql{s)FttRH17PLcCnJRc_c)TGx21hx)-_jvy1KG`YSpv~X)FvG11VFxM?jpvQnk zd0^1zZ@5i5@ZvVx0M~7VK+kRXkR#iA2ai0PKYUAK`k8_I?1;g{!h(U+RDyxjT(m(d zwa28xVV@=a@zNC)MiKcFk);lphFLndIh512)nfVOLN9*B~zB|1R-lQWAJ|v=7 znP(ld+!L>7T}rLV*JRmQ`%jRp{W~s{QHte!NYc2zJLq6v3)U#FHI0zy`;eKNKNetZ z5gp)V;ZB>&XY>V@QTU~Yn!d#NlCI#!n!L0-@8GGYELd|c*0iaTa?15dKT?ylA4vp1 zbab@D`qS_{G5mgNiZUTyLve;!6&OH?b(tmp+d0`-7&F|sJ7%Qf={(QoZr@n*YkR1e`OmAwN#7n5Aja z`i%l8r51BU%YyO7mc9@1{cc)kgZxbL0aj`cW2;u_fLws*WXl|}MsbeVW;@=93(AvI z1Z+(DC>g0i{exPxF^>QWt}lgAq$2m$*z4KT;N$rvfya+qCB8Kkx4*~gF2gtF8@;FA zF0@fA5waE4?n}(0NA7tN6Xq2Bl40){aUW-5qbFh;+)BC}lkD+)oA5uS4=sZ5cQfUL z4Fcw9A`;pjh6_6+_7=Sd#(D(?D$E6peO6>#~1bK zWtA%?VTvdiWv*dEXl!5g?KDByxuY~%$facG_j_D=peK55>^mPi1vQ;5kbs@@-=73m zW~$HUC#LxIPsLrWrpUHkY8Kva+nImKvED^RJm|iqE@!q2d$!){*_+S#A4uA(thG*S z`en83^L^WMI$i%!j%k4JKXdXSj9NN#uDSebH^iQ4ELsLpPa zBOAi99MTkk4QQAq(F=cS=y1kjZf`csAOutWA1s zAtdlbQ`~rSKr@%#WQwNrNH%QTe|lO^!S@}%&F-nv&F46}igR?`x>{HGIe+COgf}{1 z-%NUhkVk)h&*_)<9M_Nr>G#}+5BTT;K838& zEM{jFYqXBp*~aWV$?Tk^7i^^$jLz70ifxV@7#qg>&f03w3tqEa(t*z((QYSk#+kcw zbM2W@w*gU1Kyb6z{M|Dp7N2~a#BiP;ck}01NPXOX!US($HJH2Zd9P1w-#T4%13g6~ z#ww%=_o?o)GB^WfwrU3Uv{ck~Smm}#!tjG@Mv2?;3SPrss0G!cp75C)=R{MADcU3F zMP<7~Iy|PN=WOREMmq|wItkn;v1+mTdf~YU9+iCc4S9@Yzp&%!uDr)56QX7r!-*Y^ z4TA1Z3F7lF%|Th_&WqYnPN(K{_dK+fm+g~DFcBH{<;hISH(^+|! zo`KboMxFCVC1Jd2^(PAsGNz^z6{Oz$_2O8&dqzi>@*zMaYr!kq?b#fVrwyoL2Um_C~)V$l$p^0_2F!7X>BPl5fr#PPJVNd^i_4Lfs zQg^l+XxU>vC<$46ldE?p?6)xQ7IfNk5%K8^Z(HlM)^__{oYWR9W~cVP)%gQ|qA6jF zC<}1!|5KpS<_lvXIQ&46A~s$2Hg5925G7-co<}0>{s-uwI zec#3ilNTX>zCu3u?C~i)W%qD>m3rYZ=V13Kg43OP_0h+L@4yd-kFywrsNxV;HA$R% zMyFf)Z=a869qxl4(hjIJhkcK4>n#2DJ6|bRRV1q|gFOD2+6E0Hw#OWJrOLCoOyR_v zGAX$708#*UhBMotw`So5Lo(1>81^-gzfKzdp;C7TjUXT1lhgn+ScUK%0@#hxiC z&*$f9Y?16f4r|H|TKX$dM;L71`qb_3rJ3M-xeZ7$*BiLqH#^Ijvw5$Kn%+ahjx!>- zxJahjoWH7TBnLj8ldFeV0tq-~IKo(TskIkcy|lf%KS+7 zd~(aWeTcMWJ^cP2gy1DHpeTLfUmb?D8@vv1&1x<)9F%DN4|=O0qN`&uWDQV7h;p%*2%_iA5dBaIkVgY%_q# zZhmvN(6jy;FBf+$Q>&I4T+1ZKpcwA9ohSp02oGaFZjLa1szjLv1JTz`~rxpWz?0@U&E^)5XJwX!1W$&*=ViZq}=Z-MCGwzkSFhM+R?y(oejrvSP!e?bl)Pd)<#;7M@` z-Y`zwalJ~|2QA55x`^RZQt;@FmCPM&d@*lb339apLLRYg10H+QrzVroqOUq>c#{S+ zqx>sq<%xD)L_fg?$P0zjTOv)(0iOck3Nu4BM+JUvNt8$AN|z zbJ4eg5U~Ii=LTF0R)z=se4E|z->v2v=gkG9`t>3mVAPUfWL(xsMwFJC)<)QLoWe2d zP_zXz9-#Vt0y8mL)_Mre3A9HGn)i}M@0KO8OHQ@R%UmbP&GBO1u(X!!lhCKe?bjol zsD2%Ifv;YPf3SKfY-8=pGuNSB;zwDf%`lDuYFJ)F5ilz(n2|(iD5D;5rAv=xgg)J| zwEiF9H4&!pTpOxO=GUQ}oS`ywzDz$M3s`HV*Co4j(+}?OH7bMfFv=%zN<5728QV}L ztX3{`BQGkW&2i?Yiy*d=oq1qpk6s#OdD3Y`(xVqBa z?vX&DjnXIU>5Qm--2_|K{Qi%4LJk`JrUe;7D+VfiTaXhABNPj1#gAz8GaR~dAd!hi zPLK=KhVVw>8Z1zjQb-J!&ReDu;;EoFACC{;117o!gW=L4(=()yM>oTH$br|ms!!(e z@z5{YV+Jgq;9gXuGU)NAE76qPS!UkH**M5-j`^fXA*1dtiGI2n)USZ2W{4y=`kKVi zGK94)y&hnQE21kHg-n|Gg(vCg6&iM$(MQ3Y&tT7B-@X^oH8fZvc&L+~ha;61 z{<4VC6r|T04F=^U=KK&>D3{K2BofNKc>nFah@n@L`w-_*duK2&F=?N1I8X!5MbJx! z$bsxd1+)-*5jk^%4&VwHwu0;VczUT?>=nYL;G5xQ^l32AoQpbZpkX!)DfS9_!Y~0? zE|Vq-D#gB-hUKcZ`tM6w|GglMsuj}HK=ujg$>|%dni@Fr*dyq^w5n$i*!di&2erQ( zbe~J7vQrfPRQsJel)Juj7IWT+EvgR}pJLBtM40_BOjIVP(WP8kBXobXN3;f<8>&SsYdcXJZnVbLV{H{On z^`K9yTcO<0FlThgauj#ip`NmH_@``Od-6mL=Dfv!`*GsG{isC_ta-I@xtr_Fj9VSVx4(YPlhD>!?;8XNY)%EW!vcy8aKfC|0lSg=ucUi$n+Wzvz5A@HJlyOt z?$vXKlhTB!cxgFw`7o=u=Ah0_dIWEGH&Y?Dq<42tMA~_9c)aEy4YzwoSD%6tx*#ni zENncBUb3#39!_&ALz}g;MoH5nESDtUmYsI;gl!5C|F-sl=FZ*1k(AuWOk<`|KXu=C(HyLdVwe!Q`AvXP6P z`DQL&i9VUw!RyOUo}G(&ZL&Vdjj^q|v(9ABRvh^W?Z$A|;ryQ5^vYzeB@c&C4dKQ< zCs&3otXVr!zdHv=OJjBOF8$87J^|?HFpJiG4dk8Ou<-->8QO+P>_nF%syy!Es_a<$ z;p7a#Z@`i*Ed7ThbO`e*FZmibFwPCwgaW=gK?N_-Cx(#7kQ1>uw_0izE0Qe+Y}s`0 zpdeg`$7AqWKHbM$y1|&(;4z)n_h8WEIae+YbV|ZfWIn6|Kg_-fq(4~?LqM>1B)JjW z#(Cj!2O&Cx9mk;Q8NVHtIkkod4ykDp;)2J`al;HHFlp1yS)r}wzaKXJ?eO00->z*J ze-wd%z)$-ZpQQ;l4RM+%Slp zeFOw%7|4a)CWLDBS9y=>dh~nS6MrwW_xFp>JBT9m@vd8Ko|2FyjQ|r z1)@W%ZSW}uMH6$g=GO}2A5g1w7*S)3B!Oznh0R*4KD;(fhtn$JlfZ7*Kw-C>1MCz1 z^yqBpNFQRc9cl)!x^!;T_TM z7j}NO`8KF>0~xi@_;!OJ*8k5BGXm^}x=rbU3jBak*%{UAZ2TJr+ar1M+zng3u7;g% zcaGoVuqgCtkRUx(>9b+Es|tLYwoO!v{HLlAH{)xHypB5zwM+i&)b0SYGOJ~C zD!`fD^OvtZH!qCkZ~!XdE@zmfyC!pP^?yyw=33V@it^ROCM5W!|_jf zKf0^ou@Gz21}HxA(p8!)AOj-5%AOS>d0Uzl6CJ;s4 zaW5~UTP~vsm(h%tVl)L!WwE<&QH1g~YfrySJq~Zfn*+DLu*RdH)rsDXy(hr{IS`OE zWDt7_s4Pe|N;TS-$Ve`*GgXF%J#zFO>fouFxr{^Nngd5bws~pNW>V2dl%rwUMqn!j z19A?W<2!hRG~0Ep2}?@#PPK?~v+_oe`)?xaRIt&RMibE>CqdF#8{Ud?(+1d%-C~%y zEG9*!AD_$0z8cV?12sJax@QLD)+X(;e)?=)QOo8P1suEH7?u$yc7Ty7oJ6*z z$iyPu+1U`kBxo|6O@3@aXDLU0P1aVs&!y{mIq`wy!()h>?oXX#q6g5$AQh>dXVU|4 zULkwl5=ko=LI2|=>UL{IlU4pLJ_<@ThYy0g!{~oH;l981Maj>;FxAxS%2*#vZCJzEm3b=gIX0aJ zQ?oqR6j5|Y@uP8io1=2T!Z~PlWXXKX#-#NPSDppbKTzVg9mo3ywy}EOl;mfONsCQ- ze9LxbLG_TSUs!GzknS{`H|vl}3t&hW7KlNbH{;3%!90or&kLT8;V0IZ`vE@4w2!?= z)YDNUlm`h?`aSf5K;4dBbgyuZ&%7{v3=CenC`kPin4MbbBNO-u{tsnzewZ(qRPa~b z{HO-)oAEs@NjwL&3z>%=#$(lY7BY!%SoBV1S5K6rR!H25jQWCd;AJb`VF>RwguMI) z$efnT^=FxUM)yu5Y&@$^m|4l%hG$zIjBr2xrigKfX?-Nf;Gy% z3X~0+^s*B=8Y~W&e(#8!#cQcZpLsALnk&tXnK& zLDbZuYu0vNpD+q~%oO>d7s(y~k7~aC-t{2x`eM$1LicXST{0AnIo}DY&OVo^eg>4u zne{R|IT~f5P>GLK;Ki@IOLN{FCjCjaF3&kVANkB@e%Y|%`rn$sKCt=KH<7>fLf0Q) z$RX9kLMP{;&Y)y1^vDFbaqj>A=0AUXbMNnO-r$jV?UO2)&SK-yU}GZ=&SihkJs#S+ zsq%X%;!*6&uWDN>%gP_AX9C;RS(|d#Uy^LC_Ppeyy72oRIr;GAe@2nH2#Sxp-}k&matnr6FkxP0t2(Zk74ILpUSi}SD1he>c$mExynv`FcH(njq#U1jE9iH+e&72!V3#w#vYH8Cv)T;P&b5>AbtZuN_j}2xI)gh>yCayJLQJeo758P4HbIe-? z6Kw7WB+Ww8RO0}1aSmU< zz)x|tTpd@RlPsnBw%bK~H3vE%R%YKGBsG&ubF4T9*qZg_uef4KnLRl_n;p4=GwHi# zO<0zEoLpLj)g?#RW&dpDQzI~I7V+HdAq&iWRM(TAMU9;ldqRV$WZbMDy> zbxYwssq|*PO={=g{?=L9-OWDltd1P;KJ?CV5s+I!u;k(FDM4_pp1^ehM)qQKLLi!av&*+~xaKzj?OB3xwTkAnfX{1pU0Kc1a~0=;b>= z;6;5eJFTZz;MI^wL}uO071Nh0S-i!MRpxt^ELO5rNwPR&RgNsmSe+tZBeYVO z)`m81_CCfgLd#~?Y+5PaVqn4MRIpWu1G*xTf>Vpm$^<*0M|}Mc_KS2_MYkNbUo4^B zD;d31ba_`CvRG3&V6zzM#md!{{*$$1-*J3Vci*3@Zu!qbv~szwLU_RHBiZre)2cXJ zz2T-+&#PW{KinCbWJEmbe;mMbfu-pz|6+0|ub{l13Ir@d9OT0MjTV#$Yh3w5iPt+D~Kw9OaQ}qylOVAr$7exsaRI^N4JagT%`U`zPD zM`0JI)jdk<$)A#C%TNAH&-=-J+X_XwerS8tewtwy|C5Zp1!9;-yT_cRdO=k6UqXsF zJ_C(I|9{o!gQhwmv33DCq`s` zkb-|l!&O}^1Yt(Pfam35f#gP@DMhAe1re9y`g)($nQ9(2AN!^ zVE?!F6l5QZH-6{Dpu?ZoXeD6({>dp7o zbcq79SuCGAJl8Uyv&OeomzunR^C(W7j4^5&nY~db=dv?XE}Y7Z!Np4IX%< z6RD8uVrk5IC2iCWRo2GPe(=0Ww~@>h=7xk+UkbvUaA)k~<{TQzf(HV{ObAahnM=2n zoDIVgt44WX8HB;4ZPcOp((P*w1~BsN7~8n=HXylbhRD!bYN{Qh*pA5`K06?~;2~Zo zEWiw`8+X|RUM7Bx2xn#d^Lk!zx^RM~G)#4?9q>AT7bl9qa!#7Svh|b?OD*=x_(Q(T z3Z&ovlo$n}KC3%Uof!TUa*CE_$JlF%%y?xoQmD>eT{TKtx?fHddG|7-b#KVm!ORe8 zHPd5XN12_-w4_%B=YaiDlk>>o;*_becekR?gFghB$=vB$2m9}RY{?uUY&;m64vcVB@2PTP$(5<2{$4B8WmnDBp{RMD z{t%K5J7}lFFqD2;8%7P0*(=V=>I7dNlC{!?AJj7#psyE`nyNYKGc<>7fgcOKB!+Hx zICx72uBq2g4~ITz{Z|P~gdAAlQrx4N_D%XJlN)j9I{#QuKlb`oY5-IoiZzh6+WP)W z5GM9LLY&@|vrF=`HxKv-X~Ndfzm$eoE~Fvz$ifL5k=hW&%{I_`7a6E=WGPh?WA-*l;a?tyQdGOxkgO?ePX!D8F$VwaKK>p0!*D?5Rn>S7b1Gkn@dJ0)PL41Ci&AwevZNqtQpD!=tk-~?i0yQYus=D%*6iIhVb2s!8d(~w0*HZW zbP;G95CNa;+1bcynOfWy$35EZO1B$Xn@qk}_MnGqno_ofmbwvfTO2%YoP)w$>-b@$ zI@}g`@vGnLJ3AM!hq%o?ndIE6xDLiBnq)KT;;R}ayG(`*Nx2_P{Jxqj9ABs|YOKS} zT#FFDUuJIxfE$n4=sGR;L@p6&Qyu>zDopOqboQkkNegxqFIi*X{6a$$!FaA3-#c(~ zsP4suP=Nt3bHT&yyB}kQxf+$L9s!2PHE-jad4gHQUjFH^qKdCBpE3Y5vXrUD#;YM8<(y-E2BKIBV_>ubJ=iErEM0NXA8 zYD^&A8cf_=+I*%;8@28B2*KMq?xg;d_**bu=J(D$bh(Sm8t*=EQ~LNpG&3m#gj0mRnc2mE=y9lJ9M3 zESCOu=$RAh6MKVR$(BVt_jbWdFd#zOlGnads2@*rkq}$kV#E$U5XXO#jx$--iF!b-DBM;XuY@#jlIH1&Wia`#AtB`A3ba~*sb z-bY6+CSD$(W0ovf!#Q&jU&+bnYYCnWeH%B6MI;^^@`5A}sd&=bc;AGM41UtL`k3zBiXj&_;~UUsCy=6uB3bJui0*B*^WOZZDf?;9ntZp|nZ(rg z;3FDoBmO-Ra<>)p(1y7^SZjK%{HUz!2zuIp)g8`))njuh%TjIyK~U48@{2?cyY zTW@*alYqAuhKFQ|LCmM|e|U~p+A9GI;etP?f%LNd)B12-(Y&4oRZRZD}&Wa}K5 zyK~Z9y=WNLrySCYkE3`_qn$2IqfZY=@7Wm=1hLtBuvYWn7i#Ed^bUAgoPs?hJ#pvW z35j(OySH2_B|2-r%K4nRzc9Q>*36Kcv8LX`4T}T4$%wm!lidEqU zh8ceHA(X8Yf7{_#-P8DMwtJ}2=zse)SQXSOmV>WV$MIRSO5l5%YK9{3;BVM&`7b;s zD7awHono4(19FmtqL5=P@Q*@6NjJdbxNXCy_}~0d9o&&SKMAbc$2TYM3=`oo{2<1HWl ztW{Y8;-)BM&4)i5=aU82 zXikKkPJ&psI0el|u2Sf8Cc7*ihLmkUxBImVXBi)0WlC%I41JK#SO9mcoXMqRq?1-D zgS>WNwVw07b!gma=S?EM{DGcwAMExzW`;BtdPWXuS>@_FmU8&e(1IY`xnfO*v z22gAqKghGCb`Z`8V?*nseIVY3w}5Zpk|R?y3{uM*@J727L-=6p;uJ_dzG9cu4>?N0 zz>25sbN!R3ZuwshF!Sk2Lzf5SjGZ0$;rLe>(fz7P;P%bi|NLm@UZin%iQ8sm%4HT$ zIC&jDy%lxlHu!pR2Qk literal 0 HcmV?d00001 From 32cdd81919ba0aee534210791907ba999c79e9bf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 00:01:23 -0500 Subject: [PATCH 45/63] OPL: fix chan osc in 4-op mode --- src/engine/platform/opl.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 2dd6d1dcf..e1ad92f5f 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1421,6 +1421,15 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) { DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { if (ch>=18) return NULL; + if (oplType==3 && ch<12) { + if (chan[ch&(~1)].fourOp) { + if (ch&1) { + return oscBuf[ch-1]; + } else { + return oscBuf[ch+1]; + } + } + } return oscBuf[ch]; } From 9685a5c0d87f00c503a0e220e29fd3f9f92229bc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 00:18:40 -0500 Subject: [PATCH 46/63] AY: add stereo separation slider --- src/engine/platform/ay.cpp | 5 +++-- src/engine/platform/ay.h | 1 + src/engine/platform/ay8930.cpp | 5 +++-- src/engine/platform/ay8930.h | 1 + src/gui/sysConf.cpp | 8 ++++++++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index f475b4750..cf52f3d29 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -115,8 +115,8 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l ay->sound_stream_update(ayBuf,len); if (stereo) { for (size_t i=0; i>8); + bufR[i+start]=((ayBuf[0][i]*stereoSep)>>8)+ayBuf[1][i]+ayBuf[2][i]; } } else { for (size_t i=0; idevice_reset(); stereo=(flags>>6)&1; + stereoSep=(flags>>8)&255; } int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 4b7ce1068..0537dadab 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -66,6 +66,7 @@ class DivPlatformAY8910: public DivDispatch { int dacPos; int dacSample; unsigned char sampleBank; + unsigned char stereoSep; int delay; diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 123dc68ac..d5f01013d 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -99,8 +99,8 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l ay->sound_stream_update(ayBuf,len); if (stereo) { for (size_t i=0; i>8); + bufR[i+start]=((ayBuf[0][i]*stereoSep)>>8)+ayBuf[1][i]+ayBuf[2][i]; } } else { for (size_t i=0; i>6)&1; + stereoSep=(flags>>8)&255; } int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 81995f2de..75a5f6974 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -66,6 +66,7 @@ class DivPlatformAY8930: public DivDispatch { DivDispatchOscBuffer* oscBuf[3]; unsigned char regPool[32]; unsigned char ayNoiseAnd, ayNoiseOr; + unsigned char stereoSep; bool bank; int delay; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 900845a69..98b600286 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -358,6 +358,14 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { copyOfFlags=(flags&(~0x40))|(stereo?0x40:0); } + if (stereo) { + int sep=256-((flags>>8)&255); + if (CWSliderInt("Separation",&sep,1,256)) { + if (sep<1) sep=1; + if (sep>256) sep=256; + copyOfFlags=(flags&(~0xff00))|((256-sep)<<8); + } + } ImGui::EndDisabled(); bool clockSel=flags&0x80; ImGui::BeginDisabled((type==DIV_SYSTEM_AY8910) && ((flags&0x30)!=16)); From ac68419b785dc2e3a8d6ffe6c7b0b01c6ef5105e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 01:18:41 -0500 Subject: [PATCH 47/63] implement KVS on YM2612 --- TODO.md | 4 --- src/engine/fileOps.cpp | 12 ++++++++ src/engine/platform/fmsharedbase.h | 2 ++ src/engine/platform/genesis.cpp | 16 +++++------ src/gui/gui.h | 1 + src/gui/insEdit.cpp | 46 ++++++++++++++++++++++++++++++ 6 files changed, 69 insertions(+), 12 deletions(-) diff --git a/TODO.md b/TODO.md index 84bc08a73..8b1378917 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1 @@ -# to-do for 0.6pre1.5 -- stereo separation control for AY -- KVS -- bug fixes diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 4c213744b..7da21f4fa 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -196,6 +196,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } */ + // Genesis detuned on Defle v10 and earlier + /*if (ds.version<19 && ds.system[0]==DIV_SYSTEM_GENESIS) { + ds.tuning=443.23; + }*/ + // C64 detuned on Defle v11 and earlier + /*if (ds.version<21 && (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580)) { + ds.tuning=433.2; + }*/ + logI("reading module data..."); if (ds.version>0x0c) { ds.subsong[0]->hilightA=reader.readC(); @@ -449,6 +458,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ins->fm.op[j].ssgEnv=reader.readC(); } } + if (ds.version<0x12) { // before version 10 all ops were responsive to volume + ins->fm.op[j].kvs=1; + } logD("OP%d: AM %d AR %d DAM %d DR %d DVB %d EGT %d KSL %d MULT %d RR %d SL %d SUS %d TL %d VIB %d WS %d RS %d DT %d D2R %d SSG-EG %d",j, ins->fm.op[j].am, diff --git a/src/engine/platform/fmsharedbase.h b/src/engine/platform/fmsharedbase.h index 640997392..15baacefa 100644 --- a/src/engine/platform/fmsharedbase.h +++ b/src/engine/platform/fmsharedbase.h @@ -23,6 +23,8 @@ #include "../dispatch.h" #include +#define KVS(x,y) ((chan[x].state.op[y].kvs==2 && isOutput[chan[x].state.alg][y]) || chan[x].state.op[y].kvs==1) + class DivPlatformFMBase: public DivDispatch { protected: const bool isOutput[8][4]={ diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 515f0c0ae..d289a3515 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -254,7 +254,7 @@ void DivPlatformGenesis::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -327,7 +327,7 @@ void DivPlatformGenesis::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -384,7 +384,7 @@ void DivPlatformGenesis::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -501,7 +501,7 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) { if (isMuted[ch]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[ch].state.alg][j]) { + if (KVS(ch,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -614,7 +614,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][i]) { + if (KVS(c.chan,i)) { if (!chan[c.chan].active || chan[c.chan].insChanged) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } @@ -687,7 +687,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][i]) { + if (KVS(c.chan,i)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -860,7 +860,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][c.value]) { + if (KVS(c.chan,c.value)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -1051,7 +1051,7 @@ void DivPlatformGenesis::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/gui/gui.h b/src/gui/gui.h index 77a127595..80b550401 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1603,6 +1603,7 @@ class FurnaceGUI { void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType); void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size); void drawSysConf(int chan, DivSystem type, unsigned int& flags, bool modifyOnChange); + void kvsConfig(DivInstrument* ins); // these ones offer ctrl-wheel fine value changes. bool CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 31dfdb357..d08e276a7 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1211,6 +1211,46 @@ inline bool enBit30(const int val) { return false; } + +void FurnaceGUI::kvsConfig(DivInstrument* ins) { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("(click to configure KVS)"); + } + int opCount=4; + if (ins->type==DIV_INS_OPLL) opCount=2; + if (ins->type==DIV_INS_OPL) opCount=(ins->fm.ops==4)?4:2; + if (ImGui::BeginPopupContextItem("IKVSOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("operator level changes with volume?"); + if (ImGui::BeginTable("KVSTable",4,ImGuiTableFlags_BordersInner)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch); + for (int i=0; i<4; i++) { + int o=(opCount==4)?orderedOps[i]:i; + if (!(i&1)) ImGui::TableNextRow(); + const char* label="AUTO##OPKVS"; + if (ins->fm.op[o].kvs==0) { + label="NO##OPKVS"; + } else if (ins->fm.op[o].kvs==1) { + label="YES##OPKVS"; + } + ImGui::TableNextColumn(); + ImGui::Text("%d",i+1); + ImGui::TableNextColumn(); + ImGui::PushID(o); + if (ImGui::Button(label,ImVec2(ImGui::GetContentRegionAvail().x,0.0f))) { + if (++ins->fm.op[o].kvs>2) ins->fm.op[o].kvs=0; + PARAMETER; + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + ImGui::EndPopup(); + } +} + void FurnaceGUI::drawMacros(std::vector& macros) { float asFloat[256]; int asInt[256]; @@ -1769,6 +1809,7 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + kvsConfig(ins); break; case DIV_INS_OPZ: ImGui::TableNextColumn(); @@ -1781,6 +1822,8 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar(FM_NAME(FM_AMS2),ImGuiDataType_U8,&ins->fm.ams2,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + kvsConfig(ins); + if (ImGui::Button("Request from TX81Z")) { doAction(GUI_ACTION_TX81Z_REQUEST); } @@ -1813,6 +1856,7 @@ void FurnaceGUI::drawInsEdit() { } ImGui::TableNextColumn(); drawAlgorithm(ins->fm.alg&algMax,fourOp?FM_ALGS_4OP_OPL:FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + kvsConfig(ins); break; } case DIV_INS_OPLL: { @@ -1837,6 +1881,8 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndDisabled(); ImGui::TableNextColumn(); drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); + kvsConfig(ins); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); bool isPresent[4]; From 89916de0672461e8c6208cd7cd9759ea6895fce6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 01:18:53 -0500 Subject: [PATCH 48/63] remove to-do list --- TODO.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 TODO.md diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 8b1378917..000000000 --- a/TODO.md +++ /dev/null @@ -1 +0,0 @@ - From b053d31a6dc629f2d0f3b312e7517cba0fc45441 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 01:30:51 -0500 Subject: [PATCH 49/63] implement KVS on the rest of FM chips --- src/engine/platform/arcade.cpp | 14 +++++++------- src/engine/platform/genesisext.cpp | 8 ++++---- src/engine/platform/opl.cpp | 25 ++++++++++++++++--------- src/engine/platform/tx81z.cpp | 16 ++++++++-------- src/engine/platform/ym2203.cpp | 16 ++++++++-------- src/engine/platform/ym2203ext.cpp | 2 +- src/engine/platform/ym2608.cpp | 14 +++++++------- src/engine/platform/ym2608ext.cpp | 4 ++-- src/engine/platform/ym2610.cpp | 14 +++++++------- src/engine/platform/ym2610b.cpp | 14 +++++++------- src/engine/platform/ym2610bext.cpp | 4 ++-- src/engine/platform/ym2610ext.cpp | 4 ++-- src/gui/insEdit.cpp | 1 - 13 files changed, 71 insertions(+), 65 deletions(-) diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 6e925c9a0..c86f806e1 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -151,7 +151,7 @@ void DivPlatformArcade::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]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -231,7 +231,7 @@ void DivPlatformArcade::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -289,7 +289,7 @@ void DivPlatformArcade::tick(bool sysTick) { } if (m.tl.had) { op.tl=127-m.tl.val; - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -390,7 +390,7 @@ int DivPlatformArcade::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 (KVS(c.chan,i)) { if (!chan[c.chan].active || chan[c.chan].insChanged) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } @@ -448,7 +448,7 @@ int DivPlatformArcade::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 (KVS(c.chan,i)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -545,7 +545,7 @@ int DivPlatformArcade::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]) { + if (KVS(c.chan,c.value)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -746,7 +746,7 @@ void DivPlatformArcade::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]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index a2df33c41..84b906bec 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -209,7 +209,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { op.tl=c.value2; if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); - } else if (isOutput[chan[2].state.alg][c.value]) { + } else if (KVS(2,c.value)) { rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); @@ -392,7 +392,7 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) { if (isOpMuted[ch-2]) { rWrite(baseAddr+0x40,127); immWrite(baseAddr+0x40,127); - } else if (isOutput[chan[2].state.alg][ordch]) { + } else if (KVS(2,ordch)) { rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127)); immWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127)); } else { @@ -526,7 +526,7 @@ void DivPlatformGenesisExt::forceIns() { if (i==2 && extMode) { // extended channel if (isOpMuted[j]) { rWrite(baseAddr+0x40,127); - } else if (isOutput[chan[i].state.alg][j]) { + } else if (KVS(i,j)) { rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); @@ -535,7 +535,7 @@ void DivPlatformGenesisExt::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index e1ad92f5f..65a195048 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -26,6 +26,8 @@ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } +#define KVSL(x,y) ((chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==2 && isOutputL[ops==4][chan[x].state.alg][y]) || chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==1) + #define CHIP_FREQBASE chipFreqBase // N = invalid @@ -138,6 +140,11 @@ const bool isOutputL[2][4][4]={ #undef N +const int orderedOpsL1[2][4]={ + {0, 1, 0, 1}, // 2-op + {0, 2, 1, 3} // 4-op +}; + const int orderedOpsL[4]={ 0,2,1,3 }; @@ -288,7 +295,7 @@ void DivPlatformOPL::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) { + if (KVSL(i,j) || i>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); @@ -406,7 +413,7 @@ void DivPlatformOPL::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) { + if (KVSL(i,j) || i>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); @@ -610,7 +617,7 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) { if (isMuted[ch]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>melodicChans) { + if (KVSL(ch,i) || ch>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); @@ -783,7 +790,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) { + if (KVSL(c.chan,i) || c.chan>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); @@ -899,7 +906,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) { + if (KVSL(c.chan,i) || c.chan>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); @@ -1048,7 +1055,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) { + if (KVSL(c.chan,c.value) || c.chan>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); @@ -1278,7 +1285,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) { + if (KVSL(c.chan,i) || c.chan>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); @@ -1295,7 +1302,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) { + if (KVSL(c.chan,c.value) || c.chan>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); @@ -1372,7 +1379,7 @@ void DivPlatformOPL::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) { + if (KVSL(i,j) || i>melodicChans) { rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 1c6470fc4..fa29cda60 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -112,7 +112,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -179,7 +179,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -232,7 +232,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -324,7 +324,7 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) { if (isMuted[ch]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[ch].state.alg][i]) { + if (KVS(ch,i)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -353,7 +353,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][i]) { + if (KVS(c.chan,i)) { if (!chan[c.chan].active || chan[c.chan].insChanged) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } @@ -419,7 +419,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][i]) { + if (KVS(c.chan,i)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -519,7 +519,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][c.value]) { + if (KVS(c.chan,c.value)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -811,7 +811,7 @@ void DivPlatformTX81Z::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index 1c3488006..13fc014d8 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -220,7 +220,7 @@ void DivPlatformYM2203::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -261,7 +261,7 @@ void DivPlatformYM2203::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -310,7 +310,7 @@ void DivPlatformYM2203::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -427,7 +427,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][i]) { + if (KVS(c.chan,i)) { if (!chan[c.chan].active || chan[c.chan].insChanged) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } @@ -487,7 +487,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][i]) { + if (KVS(c.chan,i)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -572,7 +572,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { if (isMuted[c.chan]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[c.chan].state.alg][c.value]) { + if (KVS(c.chan,c.value)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -764,7 +764,7 @@ void DivPlatformYM2203::muteChannel(int ch, bool mute) { if (isMuted[ch]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[ch].state.alg][j]) { + if (KVS(ch,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -781,7 +781,7 @@ void DivPlatformYM2203::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index c7080d431..527a8be6b 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -442,7 +442,7 @@ void DivPlatformYM2203Ext::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 42bea41b2..42a3235d9 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -376,7 +376,7 @@ void DivPlatformYM2608::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]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -421,7 +421,7 @@ void DivPlatformYM2608::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -475,7 +475,7 @@ void DivPlatformYM2608::tick(bool sysTick) { } if (m.tl.had) { op.tl=127-m.tl.val; - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -698,7 +698,7 @@ int DivPlatformYM2608::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 (KVS(c.chan,i)) { if (!chan[c.chan].active || chan[c.chan].insChanged) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } @@ -779,7 +779,7 @@ int DivPlatformYM2608::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 (KVS(c.chan,i)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -890,7 +890,7 @@ int DivPlatformYM2608::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]) { + if (KVS(c.chan,c.value)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -1093,7 +1093,7 @@ void DivPlatformYM2608::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]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 116f6b95c..7f49ed9d6 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -442,7 +442,7 @@ void DivPlatformYM2608Ext::forceIns() { if (i==2) { // extended channel if (isOpMuted[j]) { rWrite(baseAddr+0x40,127); - } else if (isOutput[chan[i].state.alg][j]) { + } else if (KVS(i,j)) { rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); @@ -451,7 +451,7 @@ void DivPlatformYM2608Ext::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 6a8509d55..34a402e2b 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -417,7 +417,7 @@ void DivPlatformYM2610::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]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -462,7 +462,7 @@ void DivPlatformYM2610::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -516,7 +516,7 @@ void DivPlatformYM2610::tick(bool sysTick) { } if (m.tl.had) { op.tl=127-m.tl.val; - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -742,7 +742,7 @@ int DivPlatformYM2610::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 (KVS(c.chan,i)) { if (!chan[c.chan].active || chan[c.chan].insChanged) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } @@ -823,7 +823,7 @@ int DivPlatformYM2610::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 (KVS(c.chan,i)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -934,7 +934,7 @@ int DivPlatformYM2610::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]) { + if (KVS(c.chan,c.value)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -1137,7 +1137,7 @@ void DivPlatformYM2610::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]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 31cd7b3ff..bbdb3c60d 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -400,7 +400,7 @@ void DivPlatformYM2610B::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]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -445,7 +445,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -499,7 +499,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { } if (m.tl.had) { op.tl=127-m.tl.val; - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -724,7 +724,7 @@ int DivPlatformYM2610B::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 (KVS(c.chan,i)) { if (!chan[c.chan].active || chan[c.chan].insChanged) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } @@ -805,7 +805,7 @@ int DivPlatformYM2610B::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 (KVS(c.chan,i)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -916,7 +916,7 @@ int DivPlatformYM2610B::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]) { + if (KVS(c.chan,c.value)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); @@ -1119,7 +1119,7 @@ void DivPlatformYM2610B::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]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index f55e6561c..a13598554 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -442,7 +442,7 @@ void DivPlatformYM2610BExt::forceIns() { if (i==2 && extMode) { // extended channel if (isOpMuted[j]) { rWrite(baseAddr+0x40,127); - } else if (isOutput[chan[i].state.alg][j]) { + } else if (KVS(i,j)) { rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); @@ -451,7 +451,7 @@ void DivPlatformYM2610BExt::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 6cee242f4..66090514f 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -442,7 +442,7 @@ void DivPlatformYM2610Ext::forceIns() { if (i==1 && extMode) { // extended channel if (isOpMuted[j]) { rWrite(baseAddr+0x40,127); - } else if (isOutput[chan[i].state.alg][j]) { + } else if (KVS(i,j)) { rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); @@ -451,7 +451,7 @@ void DivPlatformYM2610Ext::forceIns() { if (isMuted[i]) { rWrite(baseAddr+ADDR_TL,127); } else { - if (isOutput[chan[i].state.alg][j]) { + if (KVS(i,j)) { rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index d08e276a7..08b2f509c 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1881,7 +1881,6 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndDisabled(); ImGui::TableNextColumn(); drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); - kvsConfig(ins); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); From 02d207716270c9c76ef2e9889253addca461b7ba Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 02:46:55 -0500 Subject: [PATCH 50/63] MIDI out: turn notes off on reset() --- src/engine/engine.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index bb03eda18..217cc977e 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2000,6 +2000,14 @@ void DivEngine::recalcChans() { } void DivEngine::reset() { + if (output) if (output->midiOut!=NULL) { + output->midiOut->send(TAMidiMessage(TA_MIDI_MACHINE_STOP,0,0)); + for (int i=0; i=0) { + output->midiOut->send(TAMidiMessage(0x80|(i&15),chan[i].curMidiNote,0)); + } + } + } for (int i=0; idispatch(DivCommand(DIV_CMD_GET_VOLMAX,dispatchChanOfChan[i]))<<8)|0xff; From cec31b23de970448fc9bb857f24682302d7d7bde Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 04:04:32 -0500 Subject: [PATCH 51/63] GUI: temporarily disable InputText undo/redo issue #624 --- extern/imgui_patched/imgui_widgets.cpp | 15 +++++++++--- extern/imgui_patched/imstb_textedit.h | 33 ++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/extern/imgui_patched/imgui_widgets.cpp b/extern/imgui_patched/imgui_widgets.cpp index 93dfd5343..d6ddfdb88 100644 --- a/extern/imgui_patched/imgui_widgets.cpp +++ b/extern/imgui_patched/imgui_widgets.cpp @@ -3713,7 +3713,12 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const Im { const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int text_len = obj->CurLenW; - IM_ASSERT(pos <= text_len); + if (pos > text_len) { + printf("failing STB_TEXTEDIT_INSERTCHARS assertion! oh man...\n"); + obj->Edited = true; // ??? + obj->ClearText(); + return false; + } const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA)) @@ -3776,8 +3781,11 @@ static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* st state->cursor = text_len; state->has_preferred_x = 0; return; + } else { + state->cursor = 0; + printf("STB_TEXTEDIT_INSERTCHARS fail!\n"); } - IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace() + //IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace() } } // namespace ImStb @@ -3962,7 +3970,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; - const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; + // https://github.com/tildearrow/furnace/issues/624 + const bool is_undoable = 0; //(flags & ImGuiInputTextFlags_NoUndoRedo) == 0; const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; if (is_resizable) IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! diff --git a/extern/imgui_patched/imstb_textedit.h b/extern/imgui_patched/imstb_textedit.h index 75a159dac..d93642305 100644 --- a/extern/imgui_patched/imstb_textedit.h +++ b/extern/imgui_patched/imstb_textedit.h @@ -717,6 +717,9 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta state->cursor += len; state->has_preferred_x = 0; return 1; + } else { + printf("stb_textedit_paste_internal failed.\n"); + state->cursor=0; } // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details) return 0; @@ -746,6 +749,9 @@ retry: if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { ++state->cursor; state->has_preferred_x = 0; + } else { + printf("key failed: first section.\n"); + state->cursor=0; } } else { stb_textedit_delete_selection(str,state); // implicitly clamps @@ -753,6 +759,9 @@ retry: stb_text_makeundo_insert(state, state->cursor, 1); ++state->cursor; state->has_preferred_x = 0; + } else { + printf("key failed: second section.\n"); + state->cursor=0; } } } @@ -1275,14 +1284,22 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); } + bool steFailed=false; + // check type of recorded action: if (u.insert_length) { // easy case: was a deletion, so we need to insert n characters - STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); + if (!STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length)) { + printf("undo u.insert_length failed\n"); + state->cursor=0; + steFailed=true; + } s->undo_char_point -= u.insert_length; } - state->cursor = u.where + u.insert_length; + if (!steFailed) { + state->cursor = u.where + u.insert_length; + } s->undo_point--; s->redo_point--; @@ -1327,13 +1344,21 @@ static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); } + bool steFailed=false; + if (r.insert_length) { // easy case: need to insert n characters - STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); + if (!STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length)) { + printf("redo insert char failed\n"); + state->cursor=0; + steFailed=true; + } s->redo_char_point += r.insert_length; } - state->cursor = r.where + r.insert_length; + if (!steFailed) { + state->cursor = r.where + r.insert_length; + } s->undo_point++; s->redo_point++; From b9d8d91ca7b7e2b8e74b877487d63b4f5bf45de1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 04:10:12 -0500 Subject: [PATCH 52/63] GUI: I am done look I need to sleep --- extern/imgui_patched/imgui.h | 2 +- extern/imgui_patched/imgui_widgets.cpp | 3 +-- src/gui/songInfo.cpp | 8 ++++---- src/gui/songNotes.cpp | 2 +- src/gui/subSongs.cpp | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/extern/imgui_patched/imgui.h b/extern/imgui_patched/imgui.h index e9ccea9b1..3d600ea2b 100644 --- a/extern/imgui_patched/imgui.h +++ b/extern/imgui_patched/imgui.h @@ -1042,7 +1042,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_AlwaysOverwrite = 1 << 13, // Overwrite mode ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' - ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). + ImGuiInputTextFlags_UndoRedo = 1 << 16, // Enable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) ImGuiInputTextFlags_CallbackEdit = 1 << 19 // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) diff --git a/extern/imgui_patched/imgui_widgets.cpp b/extern/imgui_patched/imgui_widgets.cpp index d6ddfdb88..4579400d4 100644 --- a/extern/imgui_patched/imgui_widgets.cpp +++ b/extern/imgui_patched/imgui_widgets.cpp @@ -3970,8 +3970,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; - // https://github.com/tildearrow/furnace/issues/624 - const bool is_undoable = 0; //(flags & ImGuiInputTextFlags_NoUndoRedo) == 0; + const bool is_undoable = (flags & ImGuiInputTextFlags_UndoRedo) != 0; const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; if (is_resizable) IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index 5b113c641..8d4e0999e 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -39,7 +39,7 @@ void FurnaceGUI::drawSongInfo() { ImGui::TableNextColumn(); float avail=ImGui::GetContentRegionAvail().x; ImGui::SetNextItemWidth(avail); - if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED + if (ImGui::InputText("##Name",&e->song.name,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED updateWindowTitle(); } if (e->song.insLen==2) { @@ -61,7 +61,7 @@ void FurnaceGUI::drawSongInfo() { ImGui::Text("Author"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); - if (ImGui::InputText("##Author",&e->song.author)) { + if (ImGui::InputText("##Author",&e->song.author,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED; } @@ -70,7 +70,7 @@ void FurnaceGUI::drawSongInfo() { ImGui::Text("Album"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(avail); - if (ImGui::InputText("##Category",&e->song.category)) { + if (ImGui::InputText("##Category",&e->song.category,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED; } ImGui::TableNextRow(); @@ -78,7 +78,7 @@ void FurnaceGUI::drawSongInfo() { ImGui::Text("System"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(MAX(16.0f*dpiScale,avail-autoButtonSize-ImGui::GetStyle().ItemSpacing.x)); - if (ImGui::InputText("##SystemName",&e->song.systemName)) { + if (ImGui::InputText("##SystemName",&e->song.systemName,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED; updateWindowTitle(); e->song.autoSystem=false; diff --git a/src/gui/songNotes.cpp b/src/gui/songNotes.cpp index 74f4ed7ff..338c7da24 100644 --- a/src/gui/songNotes.cpp +++ b/src/gui/songNotes.cpp @@ -30,7 +30,7 @@ void FurnaceGUI::drawNotes() { } if (!notesOpen) return; if (ImGui::Begin("Song Comments",¬esOpen,globalWinFlags)) { - ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail()); + ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail(),ImGuiInputTextFlags_UndoRedo); } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES; ImGui::End(); diff --git a/src/gui/subSongs.cpp b/src/gui/subSongs.cpp index c56bf6405..f076298f2 100644 --- a/src/gui/subSongs.cpp +++ b/src/gui/subSongs.cpp @@ -91,7 +91,7 @@ void FurnaceGUI::drawSubSongs() { ImGui::Text("Name"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputText("##SubSongName",&e->curSubSong->name)) { + if (ImGui::InputText("##SubSongName",&e->curSubSong->name,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED; } } From 1ceca2a509ed31022d300ea8cf18afd20dffb20a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 04:13:26 -0500 Subject: [PATCH 53/63] release v0.6pre1.5 --- papers/format.md | 1 + src/engine/engine.h | 4 ++-- src/gui/about.cpp | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/papers/format.md b/papers/format.md index acedd9945..1d4e7fc76 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 116: Furnace 0.6pre1.5 - 115: Furnace dev115 - 114: Furnace dev114 - 113: Furnace dev113 diff --git a/src/engine/engine.h b/src/engine/engine.h index 40bbbbb90..461c91c0b 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -46,8 +46,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev115" -#define DIV_ENGINE_VERSION 115 +#define DIV_VERSION "0.6pre1.5" +#define DIV_ENGINE_VERSION 116 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 diff --git a/src/gui/about.cpp b/src/gui/about.cpp index b763ab97c..7e5dfdf26 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -23,16 +23,17 @@ const char* aboutLine[]={ "tildearrow", - "is proud to present", + "is not so happy to present", "", ("Furnace " DIV_VERSION), "", "the biggest multi-system chiptune tracker!", "featuring DefleMask song compatibility.", "", - "zero disassembly.", - "just clean-room design,", - "time and dedication.", + "what a mess of a versioning scheme we have...", + "I mean it! these pre-releases are like normal releases", + "by now but only because I promised you to have SNES in", + "0.6pre2 I am doing this whole mess...", "", "> CREDITS <", "", From d354f58a7c924567e62111999888aefca8d92d8d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 04:27:18 -0500 Subject: [PATCH 54/63] really release 0.6pre1.5 --- src/gui/gui.cpp | 76 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index c374576ef..a57ccf396 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -690,11 +690,11 @@ ImVec4 FurnaceGUI::channelTextColor(int ch) { const char* defaultLayout="[Window][DockSpaceViewport_11111111]\n\ Pos=0,24\n\ -Size=1280,731\n\ +Size=1280,776\n\ Collapsed=0\n\ \n\ [Window][Debug##Default]\n\ -Pos=54,0\n\ +Pos=54,19\n\ Size=400,400\n\ Collapsed=0\n\ \n\ @@ -705,9 +705,9 @@ Collapsed=0\n\ \n\ [Window][Song Information]\n\ Pos=978,24\n\ -Size=302,217\n\ +Size=302,179\n\ Collapsed=0\n\ -DockId=0x00000004,0\n\ +DockId=0x0000000F,0\n\ \n\ [Window][Orders]\n\ Pos=0,24\n\ @@ -719,7 +719,7 @@ DockId=0x00000007,0\n\ Pos=653,24\n\ Size=323,217\n\ Collapsed=0\n\ -DockId=0x00000006,2\n\ +DockId=0x00000006,0\n\ \n\ [Window][Wavetables]\n\ Pos=653,24\n\ @@ -731,13 +731,13 @@ DockId=0x00000006,1\n\ Pos=653,24\n\ Size=323,217\n\ Collapsed=0\n\ -DockId=0x00000006,0\n\ +DockId=0x00000006,2\n\ \n\ [Window][Pattern]\n\ Pos=0,243\n\ -Size=1246,512\n\ +Size=1246,557\n\ Collapsed=0\n\ -DockId=0x0000000B,0\n\ +DockId=0x00000013,0\n\ \n\ [Window][Instrument Editor]\n\ Pos=372,102\n\ @@ -746,7 +746,7 @@ Collapsed=0\n\ \n\ [Window][Warning]\n\ Pos=481,338\n\ -Size=346,71\n\ +Size=264,86\n\ Collapsed=0\n\ \n\ [Window][Sample Editor]\n\ @@ -779,8 +779,8 @@ Size=514,71\n\ Collapsed=0\n\ \n\ [Window][Mixer]\n\ -Pos=63,55\n\ -Size=450,215\n\ +Pos=429,198\n\ +Size=453,355\n\ Collapsed=0\n\ \n\ [Window][Oscilloscope]\n\ @@ -791,7 +791,7 @@ DockId=0x0000000E,0\n\ \n\ [Window][Volume Meter]\n\ Pos=1248,243\n\ -Size=32,512\n\ +Size=32,557\n\ Collapsed=0\n\ DockId=0x0000000C,0\n\ \n\ @@ -866,9 +866,10 @@ Size=368,449\n\ Collapsed=0\n\ \n\ [Window][Register View]\n\ -Pos=847,180\n\ -Size=417,393\n\ +Pos=829,243\n\ +Size=417,557\n\ Collapsed=0\n\ +DockId=0x00000014,0\n\ \n\ [Window][New Song]\n\ Pos=267,110\n\ @@ -887,8 +888,40 @@ Size=304,40\n\ Collapsed=0\n\ DockId=0x0000000A,0\n\ \n\ +[Window][Subsongs]\n\ +Pos=978,205\n\ +Size=302,36\n\ +Collapsed=0\n\ +DockId=0x00000010,0\n\ +\n\ +[Window][Oscilloscope (per-channel)]\n\ +Pos=1095,243\n\ +Size=151,557\n\ +Collapsed=0\n\ +DockId=0x00000012,0\n\ +\n\ +[Window][Piano]\n\ +Pos=177,669\n\ +Size=922,118\n\ +Collapsed=0\n\ +\n\ +[Window][Log Viewer]\n\ +Pos=60,60\n\ +Size=541,637\n\ +Collapsed=0\n\ +\n\ +[Window][Pattern Manager]\n\ +Pos=60,60\n\ +Size=1099,366\n\ +Collapsed=0\n\ +\n\ +[Window][Chip Manager]\n\ +Pos=60,60\n\ +Size=490,407\n\ +Collapsed=0\n\ +\n\ [Docking][Data]\n\ -DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,24 Size=1280,731 Split=Y Selected=0x6C01C512\n\ +DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,24 Size=1280,776 Split=Y Selected=0x6C01C512\n\ DockNode ID=0x00000001 Parent=0x8B93E3BD SizeRef=1280,217 Split=X Selected=0xF3094A52\n\ DockNode ID=0x00000003 Parent=0x00000001 SizeRef=976,231 Split=X Selected=0x65CC51DC\n\ DockNode ID=0x00000007 Parent=0x00000003 SizeRef=345,231 HiddenTabBar=1 Selected=0x8F5BFC9A\n\ @@ -899,10 +932,17 @@ DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,24 Size=1280,731 Spl DockNode ID=0x0000000E Parent=0x00000009 SizeRef=292,105 HiddenTabBar=1 Selected=0x6D682373\n\ DockNode ID=0x0000000A Parent=0x00000005 SizeRef=292,40 HiddenTabBar=1 Selected=0x0DE44CFF\n\ DockNode ID=0x00000006 Parent=0x00000008 SizeRef=323,406 Selected=0xD2AD486B\n\ - DockNode ID=0x00000004 Parent=0x00000001 SizeRef=302,231 Selected=0x60B9D088\n\ + DockNode ID=0x00000004 Parent=0x00000001 SizeRef=302,231 Split=Y Selected=0x60B9D088\n\ + DockNode ID=0x0000000F Parent=0x00000004 SizeRef=302,179 Selected=0x60B9D088\n\ + DockNode ID=0x00000010 Parent=0x00000004 SizeRef=302,36 HiddenTabBar=1 Selected=0x723A6369\n\ DockNode ID=0x00000002 Parent=0x8B93E3BD SizeRef=1280,512 Split=X Selected=0x6C01C512\n\ - DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1246,503 CentralNode=1 HiddenTabBar=1 Selected=0xB9ADD0D5\n\ - DockNode ID=0x0000000C Parent=0x00000002 SizeRef=32,503 HiddenTabBar=1 Selected=0x644DA2C1\n\n"; + DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1246,503 Split=X Selected=0xB9ADD0D5\n\ + DockNode ID=0x00000011 Parent=0x0000000B SizeRef=1093,557 Split=X Selected=0xB9ADD0D5\n\ + DockNode ID=0x00000013 Parent=0x00000011 SizeRef=827,557 CentralNode=1 HiddenTabBar=1 Selected=0xB9ADD0D5\n\ + DockNode ID=0x00000014 Parent=0x00000011 SizeRef=417,557 Selected=0x425428FB\n\ + DockNode ID=0x00000012 Parent=0x0000000B SizeRef=151,557 HiddenTabBar=1 Selected=0x4C07BC58\n\ + DockNode ID=0x0000000C Parent=0x00000002 SizeRef=32,503 HiddenTabBar=1 Selected=0x644DA2C1\n"; + void FurnaceGUI::prepareLayout() { FILE* check; From 52cd4f15de79715d96b36eeaad5a119ad998996d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 18:33:58 -0500 Subject: [PATCH 55/63] finally fix macro speed/delay issue --- src/engine/macroInt.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index 36850818f..955481ce0 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -34,6 +34,7 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic } if (delay>0) { delay--; + had=false; return; } if (began && source.delay>0) { From cbff5f190cca6292f7a4446540677081305baeea Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 18:43:48 -0500 Subject: [PATCH 56/63] NES: implement DPCM loop --- src/engine/platform/nes.cpp | 9 ++++++--- src/engine/platform/nes.h | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 50fcd5ca6..11e0fdddb 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -320,9 +320,10 @@ void DivPlatformNES::tick(bool sysTick) { unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM; unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; if (dpcmLen>255) dpcmLen=255; + goingToLoop=parent->getSample(dacSample)->isLoopable(); // write DPCM rWrite(0x4015,15); - rWrite(0x4010,calcDPCMRate(dacRate)); + rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); rWrite(0x4012,(dpcmAddr>>6)&0xff); rWrite(0x4013,dpcmLen&0xff); rWrite(0x4015,31); @@ -330,7 +331,7 @@ void DivPlatformNES::tick(bool sysTick) { } } else { if (dpcmMode) { - rWrite(0x4010,calcDPCMRate(dacRate)); + rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); } } if (dumpWrites && !dpcmMode) addWrite(0xffff0001,dacRate); @@ -385,9 +386,10 @@ int DivPlatformNES::dispatch(DivCommand c) { unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM; unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; if (dpcmLen>255) dpcmLen=255; + goingToLoop=parent->getSample(dacSample)->isLoopable(); // write DPCM rWrite(0x4015,15); - rWrite(0x4010,calcDPCMRate(dacRate)); + rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); rWrite(0x4012,(dpcmAddr>>6)&0xff); rWrite(0x4013,dpcmLen&0xff); rWrite(0x4015,31); @@ -612,6 +614,7 @@ void DivPlatformNES::reset() { sampleBank=0; dpcmBank=0; dpcmMode=false; + goingToLoop=false; if (useNP) { nes1_NP->Reset(); diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index c00003305..85c14b0c9 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -73,6 +73,7 @@ class DivPlatformNES: public DivDispatch { bool dpcmMode; bool dacAntiClickOn; bool useNP; + bool goingToLoop; struct NESAPU* nes; xgm::NES_APU* nes1_NP; xgm::NES_DMC* nes2_NP; From 861b1cb9ca5d6a814cf137935a8a25502db02ca2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 19:02:25 -0500 Subject: [PATCH 57/63] OPLL: finally fix pitch macro --- src/engine/platform/opll.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 9485551aa..5c6981588 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -243,8 +243,9 @@ void DivPlatformOPLL::tick(bool sysTick) { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE); if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq; - if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq)+chan[i].pitch2; + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>65535) chan[i].freq=65535; + int freqt=toFreq(chan[i].freq); chan[i].freqL=freqt&0xff; if (i>=6 && properDrums) { immWrite(0x10+drumSlot[i],freqt&0xff); From fa78877dfd1277f5edb68c36e4e3ef88946fb692 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 19:12:59 -0500 Subject: [PATCH 58/63] always do UTF-8 to UTF-16 when saving layout on W indows --- extern/imgui_patched/imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp index 8b013816f..d52945d4e 100644 --- a/extern/imgui_patched/imgui.cpp +++ b/extern/imgui_patched/imgui.cpp @@ -1818,7 +1818,7 @@ ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed) ImFileHandle ImFileOpen(const char* filename, const char* mode) { -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__) +#if defined(_WIN32) // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32! const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); From 02ef001eb8104cb1255ea26f36bf228641c162f0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 19:41:36 -0500 Subject: [PATCH 59/63] MSM6295: fix mute issue --- src/engine/platform/msm6295.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index 4d32f4319..bb002d1a4 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -255,6 +255,7 @@ void DivPlatformMSM6295::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformMSM6295::Channel(); chan[i].std.setEngine(parent); + msm.voice_mute(i,isMuted[i]); } for (int i=0; i<4; i++) { chan[i].vol=8; From ed7a48884464d1762c6a86216a19f764ee882459 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 22 Sep 2022 19:46:17 -0500 Subject: [PATCH 60/63] MSM6295: reduce some CPU usage --- src/engine/platform/msm6295.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index bb002d1a4..e6e3d8a7a 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -68,9 +68,11 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t delay=w.delay; } } else { - delay--; + delay-=3; } + msm.tick(); + msm.tick(); msm.tick(); bufL[h]=msm.out()<<4; @@ -388,7 +390,7 @@ void DivPlatformMSM6295::setFlags(unsigned int flags) { chipClock=COLOR_NTSC/3.0; break; } - rate=chipClock; + rate=chipClock/3; for (int i=0; i<4; i++) { oscBuf[i]->rate=rate/22; } From 3eb37ded072b90c91e8b3f344bc7b63e52394fd3 Mon Sep 17 00:00:00 2001 From: nicco1690 <78063037+nicco1690@users.noreply.github.com> Date: Thu, 22 Sep 2022 22:28:09 -0400 Subject: [PATCH 61/63] Change extension names to be lower-case --- demos/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/README.md b/demos/README.md index 43f29d793..d07738e58 100644 --- a/demos/README.md +++ b/demos/README.md @@ -11,6 +11,6 @@ contact me or send a pull request if you want your song to be added to this coll - Nintendo covers are frowned upon - big label music covers also are discouraged -tildearrow also accepts demo songs in the .DMF format as well as the .FUR format. +tildearrow also accepts demo songs in the .dmf format as well as the .fur format. thank you for contributing! From 12d55ad99dcafb50ad3e2980aa58b3b278480635 Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 23 Sep 2022 13:50:46 +0900 Subject: [PATCH 62/63] Fix regression --- extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp index c249e5548..23b7df72b 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/x1_010/x1_010.cpp @@ -122,7 +122,7 @@ void x1_010_core::voice_t::reg_w(u8 offset, u8 data) m_flag.write(data); if (!prev_keyon && m_flag.keyon()) // Key on { - m_acc = m_flag.wavetable() ? 0 : (u32(m_start_envfreq) << 16); + m_acc = m_flag.wavetable() ? 0 : (u32(m_start_envfreq) << 17); m_env_acc = 0; } break; From d5a6d136e51d8470e9f5ba634a054ba404390631 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 23 Sep 2022 18:48:47 -0500 Subject: [PATCH 63/63] update test suite files --- test/assert_delta.c | 2 +- test/furnace-test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/assert_delta.c b/test/assert_delta.c index 6dad6992b..6ef11feae 100644 --- a/test/assert_delta.c +++ b/test/assert_delta.c @@ -34,7 +34,7 @@ int main(int argc, char** argv) { while ((totalRead=sf_readf_float(sf,buf,BUF_SIZE))!=0) { for (int i=0; i