From b3e9f53ec47f30e1c520522c4728fd1f3288983d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Jul 2022 17:57:36 -0500 Subject: [PATCH 01/38] GUI: the poll --- src/gui/songInfo.cpp | 2 - src/gui/subSongs.cpp | 130 ------------------------------------------- 2 files changed, 132 deletions(-) diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index f1428db5e..7f7e026ed 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -226,8 +226,6 @@ void FurnaceGUI::drawSongInfo() { } ImGui::EndTable(); } - - ImGui::TextWrapped("if this feels incomplete, go to Subsongs.\nthe outcome of this Song Information window will be determined by a poll on the Furnace Discord."); } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO; ImGui::End(); diff --git a/src/gui/subSongs.cpp b/src/gui/subSongs.cpp index c100600d1..c56bf6405 100644 --- a/src/gui/subSongs.cpp +++ b/src/gui/subSongs.cpp @@ -94,136 +94,6 @@ void FurnaceGUI::drawSubSongs() { if (ImGui::InputText("##SubSongName",&e->curSubSong->name)) { MARK_MODIFIED; } - - if (ImGui::BeginTable("OtherSubProps",3,ImGuiTableFlags_SizingStretchProp)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("TimeBase"); - ImGui::TableNextColumn(); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - unsigned char realTB=e->curSubSong->timeBase+1; - if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED - if (realTB<1) realTB=1; - if (realTB>16) realTB=16; - e->curSubSong->timeBase=realTB-1; - } - ImGui::TableNextColumn(); - ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speed1,e->curSubSong->speed2,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD)); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Speed"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speed1,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->speed1<1) e->curSubSong->speed1=1; - if (e->isPlaying()) play(); - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speed2,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->speed2<1) e->curSubSong->speed2=1; - if (e->isPlaying()) play(); - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Virtual Tempo"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1; - if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Numerator"); - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1; - if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Denominator (set to base tempo)"); - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Highlight"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Pattern Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int patLen=e->curSubSong->patLen; - if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED - if (patLen<1) patLen=1; - if (patLen>256) patLen=256; - e->curSubSong->patLen=patLen; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Song Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int ordLen=e->curSubSong->ordersLen; - if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED - if (ordLen<1) ordLen=1; - if (ordLen>256) ordLen=256; - e->curSubSong->ordersLen=ordLen; - if (curOrder>=ordLen) { - setOrder(ordLen-1); - } - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { - tempoView=!tempoView; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz; - if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED - if (tempoView) setHz/=2.5; - if (setHz<10) setHz=10; - if (setHz>999) setHz=999; - e->setSongRate(setHz,setHz<52); - } - if (tempoView) { - ImGui::TableNextColumn(); - ImGui::Text("= %gHz",e->curSubSong->hz); - } else { - if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) { - ImGui::TableNextColumn(); - ImGui::Text("PAL"); - } - if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) { - ImGui::TableNextColumn(); - ImGui::Text("NTSC"); - } - } - - ImGui::EndTable(); - } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SUBSONGS; ImGui::End(); From 4666a8d61494187a6192d28d25c0f425fafdb014 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Jul 2022 17:57:45 -0500 Subject: [PATCH 02/38] update export-tech.md --- papers/export-tech.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/papers/export-tech.md b/papers/export-tech.md index 3bf715469..560aab324 100644 --- a/papers/export-tech.md +++ b/papers/export-tech.md @@ -4,6 +4,13 @@ TODO +## macro data + +read length, loop and then release (1 byte). +if it is a 2-byte macro, read a dummy byte. + +then read data. + ## pattern data read sequentially. From e7108c060ba3755d5d497a0223de06a98e0dfa4b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Jul 2022 23:24:32 -0500 Subject: [PATCH 03/38] add Namco WSG section to doc/7-systems --- papers/doc/7-systems/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md index 6d428f3eb..e4b6cf5b9 100644 --- a/papers/doc/7-systems/README.md +++ b/papers/doc/7-systems/README.md @@ -26,7 +26,8 @@ this is a list of systems that Furnace supports, including each system's effects - [Seta/Allumer X1-010](x1-010.md) - [WonderSwan](wonderswan.md) - [Bubble System WSG](bubblesystem.md) -- [Namco 163](n163.md) +- [Namco C163](n163.md) +- [Namco WSG](namco.md) - [Yamaha OPL](opl.md) - [PC Speaker](pcspkr.md) - [Commodore PET](pet.md) From cd4af3c4babaa669992fbe13c94c99e46a06e4b0 Mon Sep 17 00:00:00 2001 From: Aleksi Knutsi <53163105+host12prog@users.noreply.github.com> Date: Fri, 29 Jul 2022 21:20:17 +0700 Subject: [PATCH 04/38] Update soundunit.md --- papers/doc/7-systems/soundunit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/7-systems/soundunit.md b/papers/doc/7-systems/soundunit.md index d9d0abd70..89e346558 100644 --- a/papers/doc/7-systems/soundunit.md +++ b/papers/doc/7-systems/soundunit.md @@ -25,7 +25,7 @@ This is a fantasy sound chip, used in the specs2 fantasy computer designed by ti - `17xx`: set volume sweep period low byte - `18xx`: set volume sweep period high byte - `19xx`: set cutoff sweep period low byte -- `1Axx`: set cutoff sweep period low byte +- `1Axx`: set cutoff sweep period high byte - `1Bxx`: set frequency sweep boundary - `1Cxx`: set volume sweep boundary - `1Dxx`: set cutoff sweep boundary From 1921fd17595efa7ce7ed8212c33417bd031919a6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Jul 2022 01:00:51 -0500 Subject: [PATCH 05/38] PCE: implement anti-click technology --- src/engine/platform/pce.cpp | 12 +++++++++++- src/engine/platform/pce.h | 5 ++++- src/gui/sysConf.cpp | 19 ++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index f1eb5a108..a61b61836 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -136,8 +136,9 @@ void DivPlatformPCE::updateWave(int ch) { chWrite(ch,0x04,0x5f); chWrite(ch,0x04,0x1f); for (int i=0; i<32; i++) { - chWrite(ch,0x06,chan[ch].ws.output[i]); + chWrite(ch,0x06,chan[ch].ws.output[(i+chan[ch].antiClickWavePos)&31]); } + chan[ch].antiClickWavePos&=31; if (chan[ch].active) { chWrite(ch,0x04,0x80|chan[ch].outVol); } @@ -150,6 +151,13 @@ static unsigned char noiseFreq[12]={ void DivPlatformPCE::tick(bool sysTick) { for (int i=0; i<6; i++) { + // anti-click + if (antiClickEnabled && sysTick && chan[i].freq>0) { + chan[i].antiClickPeriodCount+=(chipClock/MAX(parent->getCurHz(),1.0f)); + chan[i].antiClickWavePos+=chan[i].antiClickPeriodCount/chan[i].freq; + chan[i].antiClickPeriodCount%=chan[i].freq; + } + chan[i].std.next(); if (chan[i].std.vol.had) { chan[i].outVol=VOL_SCALE_LOG(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31); @@ -555,6 +563,8 @@ void DivPlatformPCE::setFlags(unsigned int flags) { } else { chipClock=COLOR_NTSC; } + // flags&4 will be chip revision + antiClickEnabled=!(flags&8); rate=chipClock/12; for (int i=0; i<6; i++) { oscBuf[i]->rate=rate; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 870b5218a..22a24ddf8 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -28,7 +28,7 @@ class DivPlatformPCE: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, pitch2, note; + int freq, baseFreq, pitch, pitch2, note, antiClickPeriodCount, antiClickWavePos; int dacPeriod, dacRate; unsigned int dacPos; int dacSample, ins; @@ -47,6 +47,8 @@ class DivPlatformPCE: public DivDispatch { pitch(0), pitch2(0), note(0), + antiClickPeriodCount(0), + antiClickWavePos(0), dacPeriod(0), dacRate(0), dacPos(0), @@ -69,6 +71,7 @@ class DivPlatformPCE: public DivDispatch { Channel chan[6]; DivDispatchOscBuffer* oscBuf[6]; bool isMuted[6]; + bool antiClickEnabled; struct QueuedWrite { unsigned char addr; unsigned char val; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index e54074861..365312424 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -109,6 +109,24 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool } break; } + case DIV_SYSTEM_PCE: { + sysPal=flags&1; + if (ImGui::Checkbox("Pseudo-PAL",&sysPal)) { + copyOfFlags=(flags&(~1))|(unsigned int)sysPal; + } + bool antiClick=flags&8; + if (ImGui::Checkbox("Disable anti-click",&antiClick)) { + copyOfFlags=(flags&(~8))|(antiClick<<3); + } + break; + } + case DIV_SYSTEM_GB: { + bool antiClick=flags&8; + if (ImGui::Checkbox("Disable anti-click",&antiClick)) { + copyOfFlags=(flags&(~8))|(antiClick<<3); + } + break; + } case DIV_SYSTEM_OPLL: case DIV_SYSTEM_OPLL_DRUMS: case DIV_SYSTEM_VRC7: { @@ -631,7 +649,6 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool } break; } - case DIV_SYSTEM_GB: case DIV_SYSTEM_SWAN: case DIV_SYSTEM_VERA: case DIV_SYSTEM_BUBSYS_WSG: From 007024c86f1a9e1c7a34478004497bc0bccef725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Szpilowski?= Date: Sat, 30 Jul 2022 18:47:05 +0200 Subject: [PATCH 06/38] Create MetalSlug_BaseCamp_SMS_TIA.fur New cover - MetalSlug BaseCamp (form GB Advnace) --- demos/MetalSlug_BaseCamp_SMS_TIA.fur | Bin 0 -> 1917 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/MetalSlug_BaseCamp_SMS_TIA.fur diff --git a/demos/MetalSlug_BaseCamp_SMS_TIA.fur b/demos/MetalSlug_BaseCamp_SMS_TIA.fur new file mode 100644 index 0000000000000000000000000000000000000000..dd2fa6af3138b5913f10da46d4095af644d0518c GIT binary patch literal 1917 zcmV-@2ZH!`ob6p-iyTD|ubJuD+1=S>MZpI_ZoxMpkZ{I;AYO73Jk%uQ67WHg(<~A7 zPP{*gf)M{Cq98;B^~HDn2>Rrc5Bd?*Z=fGQ$sfD7cXn!br*^t_dv3RHyF=$HJ=MRe zuI^vg^vv$%#QB5Wovqipo$a@;9lX^&aUB3Uke7`M=Py1VN}*N@0G>Oy22BtT)O*)f zWQ{(BiDj*%OzyoH<5Cz0S4F9A;81~~h*@-YlT z5s6q%S_qM-i8z6v7SzHh6fqiFL0f=IVK{C)6KE~1DnS3UfAt&rN0-g+^{vi}TYLN6 z-OlFw^2fGr-?*@O<)a#aT520Rd;7Zw+ufc0jtrRqP)iL~>T%STVT{LY>#`z(AU!$Z zdNUC12k8l2X#|aSNM3G3xTXdL^?n`cjsKxsP)Nn00+0&;?oTM%9!OS6ij`nxWfX&& zD3dbDH_G2~54|l@Tim{`7u&Jq;(I$~b~^E;_?Msu2%TtQ23TAs61Lsel`2FcI^|>{ z8oxYTOy;4D>M>bcBee{3`ZVOuhJ93Rar?SnY{!y|@9mV?S*}F%*HKACD`S_3i^)8w zSRL=zV)M==O zYTU0TxWTl$sDY2gYv*Vuh{LqS?dy869ZN30w^L?kxsuWMlgUUWjR?u3#bh2- zKheig6?=9^>ijEfy{{ukw~FnlkFbyfEh*EjsiU1Z4$~I5uj|EjEV=mJPMMwMN>ZPX zC#ff$Oj61P(U+$^F{#IOY2Or1bob=-yR!Z#t1!U!3CUes+`g_C+p*;0dpl)zmMbA0 zj3=ZsP9`LrD5gzFqaRBzIuVWb^N;M@wZ-l0da)f#F21)@W@mYl&!w|hE;mL-h~4sX z^|FxJE|gjJ7%YZAOD*Sw(7bA>?@P+b5%4h_?xIk3$J-l3JEflx?N!Sm(;rHo8n2K4 zWSJZPl=3qtMCb|O31RFHSKk%?5h3(pH&)+LpTmIYffoA{677_}Ex9GbXam1N;85e5 zmrRYfNwnLF4~0*~jVmVInCNo}#MP%Z z7;59JdPAyQ&Iu7aMF{Q=#obxvMTm$=2wQT(T*ysAzp-G1NXa{ls4PS17tDfEbU;o= zo?3g^%WOF#1pdgx+?`r7SrIwxeUWX|$A8{(P6%D}GjXMKbuc-T1?$ujit^_=SX zBSPrMu6{zsCkOA~m>ju2Et|wA9E?%x+neyil=b4u9@}T-e!rMJ7FBB_lN(4*k=!EM zv3_FEE`~6XZ#g0KQzlP{$-9;L4y6}JI_OK2CLJP#%^lDaVg^F!_Yl}YtQZqUT10zH z7&O6G2m5L9ao`iemS6@cVd8Tq0ps8ob}^EhLpsHA!0k-}#{@r_0bl)^j-psm^G?NN zm?%8_*dXQ2eBn;LGX5xFvS!Dm%(#zK5j1c(CKXZ4=%U+(BoD(Ad_;+@B7GR8nWkKL` z?Na)QCI2xFmct70XNiN4|Lph#A!?ow3os$B$KQ4T5h3!MCa&Wl!;zG!-(u<~Onpj@ z2^?Vw-h*&N zk^eS~(oPFJAX|i(7g1a%1osYL;pChU`aMK_!3LBq@DLsWlXfqA*$N@D8>Uw8aIG(L zWei-_u3r9dWG=q<-}^s}{{uqcUmrHO=w&Z2o17COdk>*b6SD(j94re0pKF)WPb~S5 zaj+a#fImwdY{j1&H%j02_#;AO?;$)PMsb#b5Vqp)y8nm}`aML<1h%EGp57F-w}^HM zhtj8HTlywMG_loW#?zKQ9na!+3dh>QWa@K?Lg^zy=wr35)XCMSc3vv*6wbT|O)VU1 zLMWU?i$A4rixAwtln%AoaL0uRA*Lb(cXB)-ChJE`f2!7};!oj_J1-$ut)DX28zO{$ z4>7L+*=qN)muHeQLS+AOL`&W}#f3YsklDk$9phkG-I~={JM#2)z5m{Su3h*at^NA) D#hbI` literal 0 HcmV?d00001 From 6ff51ce8f340803a743e024b33251fdf3e04baa3 Mon Sep 17 00:00:00 2001 From: Waldemar Pawlaszek Date: Sun, 31 Jul 2022 11:33:38 +0200 Subject: [PATCH 07/38] #511 Added dynamic popcnt dispatcher --- src/engine/platform/sound/lynx/Mikey.cpp | 63 +++++++++++++++--------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/src/engine/platform/sound/lynx/Mikey.cpp b/src/engine/platform/sound/lynx/Mikey.cpp index 270e21bdf..32612960b 100644 --- a/src/engine/platform/sound/lynx/Mikey.cpp +++ b/src/engine/platform/sound/lynx/Mikey.cpp @@ -26,6 +26,24 @@ #include #include +#if defined ( _MSC_VER ) +#include + +static void cpuid( int info[4], int infoType ) +{ + __cpuidex( info, infoType, 0 ); +} + +#else +#include + +static void cpuid( int info[4], int infoType ) +{ + __cpuid_count( infoType, 0, info[0], info[1], info[2], info[3] ); +} + +#endif + namespace Lynx { @@ -34,29 +52,7 @@ namespace static constexpr int64_t CNT_MAX = std::numeric_limits::max() & ~15; -#if defined ( __cpp_lib_bitops ) - -#define popcnt(X) std::popcount(X) - -#elif defined( _MSC_VER ) - -# include - -uint32_t popcnt( uint32_t x ) -{ - return __popcnt( x ); -} - -#elif defined( __GNUC__ ) - -uint32_t popcnt( uint32_t x ) -{ - return __builtin_popcount( x ); -} - -#else - -uint32_t popcnt( uint32_t x ) +uint32_t popcnt_generic( uint32_t x ) { int v = 0; while ( x != 0 ) @@ -67,7 +63,16 @@ uint32_t popcnt( uint32_t x ) return v; } +uint32_t popcnt_intrinsic( uint32_t x ) +{ +#if defined ( _MSC_VER ) + return __popcnt( x ); +#else + return __builtin_popcount( x ); #endif +} + +static uint32_t( *popcnt )( uint32_t x ); int32_t clamp( int32_t v, int32_t lo, int32_t hi ) { @@ -514,6 +519,18 @@ private: Mikey::Mikey( uint32_t sampleRate ) : mMikey{ std::make_unique() }, mQueue{ std::make_unique() }, mTick{}, mNextTick{}, mSampleRate{ sampleRate }, mSamplesRemainder{}, mTicksPerSample{ 16000000 / mSampleRate, 16000000 % mSampleRate } { enqueueSampling(); + + //detecting popcnt availability + int info[4]; + cpuid( info, 1 ); + if ( ( info[2] & ( (int)1 << 23 ) ) != 0 ) + { + popcnt = &popcnt_intrinsic; + } + else + { + popcnt = &popcnt_generic; + } } Mikey::~Mikey() From a9afcf873c5cabc7da2a491035f35ae4ed8cf9ff Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 31 Jul 2022 14:05:23 -0500 Subject: [PATCH 08/38] fix ARM build --- src/engine/platform/sound/lynx/Mikey.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/engine/platform/sound/lynx/Mikey.cpp b/src/engine/platform/sound/lynx/Mikey.cpp index 32612960b..718180033 100644 --- a/src/engine/platform/sound/lynx/Mikey.cpp +++ b/src/engine/platform/sound/lynx/Mikey.cpp @@ -26,6 +26,11 @@ #include #include +#if defined(i386) || defined(__i386__) || defined(__i386) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) +#define IS_INTEL +#endif + +#ifdef IS_INTEL #if defined ( _MSC_VER ) #include @@ -42,6 +47,7 @@ static void cpuid( int info[4], int infoType ) __cpuid_count( infoType, 0, info[0], info[1], info[2], info[3] ); } +#endif #endif namespace Lynx @@ -63,6 +69,7 @@ uint32_t popcnt_generic( uint32_t x ) return v; } +#ifdef IS_INTEL uint32_t popcnt_intrinsic( uint32_t x ) { #if defined ( _MSC_VER ) @@ -71,6 +78,7 @@ uint32_t popcnt_intrinsic( uint32_t x ) return __builtin_popcount( x ); #endif } +#endif static uint32_t( *popcnt )( uint32_t x ); @@ -521,6 +529,7 @@ Mikey::Mikey( uint32_t sampleRate ) : mMikey{ std::make_unique() }, enqueueSampling(); //detecting popcnt availability +#ifdef IS_INTEL int info[4]; cpuid( info, 1 ); if ( ( info[2] & ( (int)1 << 23 ) ) != 0 ) @@ -531,6 +540,9 @@ Mikey::Mikey( uint32_t sampleRate ) : mMikey{ std::make_unique() }, { popcnt = &popcnt_generic; } +#else + popcnt = &popcnt_generic; +#endif } Mikey::~Mikey() From 5feba3a7167b54bbebb1bba7159dcbda91b41663 Mon Sep 17 00:00:00 2001 From: Waldemar Pawlaszek Date: Sun, 31 Jul 2022 22:26:59 +0200 Subject: [PATCH 09/38] More robust popcnt --- src/engine/platform/sound/lynx/Mikey.cpp | 105 +++++++++++------------ 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/src/engine/platform/sound/lynx/Mikey.cpp b/src/engine/platform/sound/lynx/Mikey.cpp index 718180033..791336c1b 100644 --- a/src/engine/platform/sound/lynx/Mikey.cpp +++ b/src/engine/platform/sound/lynx/Mikey.cpp @@ -26,39 +26,11 @@ #include #include -#if defined(i386) || defined(__i386__) || defined(__i386) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) -#define IS_INTEL -#endif +#if defined( _MSC_VER ) -#ifdef IS_INTEL -#if defined ( _MSC_VER ) #include -static void cpuid( int info[4], int infoType ) -{ - __cpuidex( info, infoType, 0 ); -} - -#else -#include - -static void cpuid( int info[4], int infoType ) -{ - __cpuid_count( infoType, 0, info[0], info[1], info[2], info[3] ); -} - -#endif -#endif - -namespace Lynx -{ - -namespace -{ - -static constexpr int64_t CNT_MAX = std::numeric_limits::max() & ~15; - -uint32_t popcnt_generic( uint32_t x ) +static uint32_t popcnt_generic( uint32_t x ) { int v = 0; while ( x != 0 ) @@ -69,18 +41,60 @@ uint32_t popcnt_generic( uint32_t x ) return v; } -#ifdef IS_INTEL -uint32_t popcnt_intrinsic( uint32_t x ) +#if defined( _M_IX86 ) || defined( _M_X64 ) + +static uint32_t popcnt_intrinsic( uint32_t x ) { -#if defined ( _MSC_VER ) return __popcnt( x ); -#else - return __builtin_popcount( x ); -#endif } + +static uint32_t( *popcnt )( uint32_t ); + +//detecting popcnt availability on msvc intel +static void selectPOPCNT() +{ + int info[4]; + __cpuid( info, 1 ); + if ( ( info[2] & ( (int)1 << 23 ) ) != 0 ) + { + popcnt = &popcnt_intrinsic; + } + else + { + popcnt = &popcnt_generic; + } +} + +#else //defined( _M_IX86 ) || defined( _M_X64 ) + +//MSVC non INTEL should use generic implementation +inline void selectPOPCNT() +{ +} + +#define popcnt popcnt_generic + #endif -static uint32_t( *popcnt )( uint32_t x ); +#else //defined( _MSC_VER ) + +//non MVSC should use builtin implementation + +inline void selectPOPCNT() +{ +} + +#define popcnt __builtin_popcount + +#endif + +namespace Lynx +{ + +namespace +{ + +static constexpr int64_t CNT_MAX = std::numeric_limits::max() & ~15; int32_t clamp( int32_t v, int32_t lo, int32_t hi ) { @@ -526,23 +540,8 @@ private: Mikey::Mikey( uint32_t sampleRate ) : mMikey{ std::make_unique() }, mQueue{ std::make_unique() }, mTick{}, mNextTick{}, mSampleRate{ sampleRate }, mSamplesRemainder{}, mTicksPerSample{ 16000000 / mSampleRate, 16000000 % mSampleRate } { + selectPOPCNT(); enqueueSampling(); - - //detecting popcnt availability -#ifdef IS_INTEL - int info[4]; - cpuid( info, 1 ); - if ( ( info[2] & ( (int)1 << 23 ) ) != 0 ) - { - popcnt = &popcnt_intrinsic; - } - else - { - popcnt = &popcnt_generic; - } -#else - popcnt = &popcnt_generic; -#endif } Mikey::~Mikey() From fe07051f8963827568c957d315dba3f26692f528 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 1 Aug 2022 22:51:13 -0500 Subject: [PATCH 10/38] rename Envelope release to Macro release --- src/gui/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 6582a4e21..2ad3ef23c 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1712,7 +1712,7 @@ void FurnaceGUI::drawSettings() { ImGui::Text("%s",SDL_GetScancodeName((SDL_Scancode)i.scan)); ImGui::TableNextColumn(); if (i.val==102) { - snprintf(id,4095,"Envelope release##SNType_%d",i.scan); + snprintf(id,4095,"Macro release##SNType_%d",i.scan); if (ImGui::Button(id)) { noteKeys[i.scan]=0; } From 1f57d09fbf0019f93b0e40acd39607d6747bc5af Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 2 Aug 2022 23:16:42 -0500 Subject: [PATCH 11/38] GUI: display correct OPLL patch names --- src/gui/insEdit.cpp | 162 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 140 insertions(+), 22 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index f0d6d3da3..09d6f4066 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -43,24 +43,93 @@ const char* fmParamShortNames[3][32]={ {"ALG", "FB", "FMS", "AMS", "A", "D", "D2", "R", "S", "TL", "RS", "ML", "DT", "DT2", "SSG", "AM", "DAM", "DVB", "EGT", "EGS", "KSL", "SUS", "VIB", "WS", "KSR", "DC", "DM", "EGS", "REV", "Fine", "FMS2", "AMS2"} }; -const char* opllInsNames[17]={ - "User", - "Violin", - "Guitar", - "Piano", - "Flute", - "Clarinet", - "Oboe", - "Trumpet", - "Organ", - "Horn", - "Synth", - "Harpsichord", - "Vibraphone", - "Synth Bass", - "Acoustic Bass", - "Electric Guitar", - "Drums" +const char* opllVariants[4]={ + "OPLL", + "YMF281", + "YM2423", + "VRC7" +}; + +const char* opllInsNames[4][17]={ + /* YM2413 */ { + "User", + "Violin", + "Guitar", + "Piano", + "Flute", + "Clarinet", + "Oboe", + "Trumpet", + "Organ", + "Horn", + "Synth", + "Harpsichord", + "Vibraphone", + "Synth Bass", + "Acoustic Bass", + "Electric Guitar", + "Drums" + }, + // help me get the names! + /* YMF281 */ { + "User", + "Name under Register-Wall #1", + "Name under Register-Wall #2", + "Piano", + "Flute", + "Clarinet", + "Name under Register-Wall #6", + "Trumpet", + "Name under Register-Wall #8", + "Name under Register-Wall #9", + "Name under Register-Wall #10", + "Name under Register-Wall #11", + "Name under Register-Wall #12", + "Name under Register-Wall #13", + "Name under Register-Wall #14", + "Name under Register-Wall #15", + "Drums" + }, + // help me get the names! + /* YM2423 */ { + "User", + "Violin", + "What is this", + "Some kind of space sound", + "Voice maybe", + "Clarinet", + "Slap something", + "Trumpet", + "AAAAAA", + "Wow!", + "Synth", + "Synth Key", + "Vibraphone", + "Space Bass", + "Synth Bass", + "Sound like the default Defle patch", + "Drums" + }, + // stolen from FamiTracker + /* VRC7 */ { + "User", + "Bell", + "Guitar", + "Piano", + "Flute", + "Clarinet", + "Rattling Bell", + "Trumpet", + "Reed Organ", + "Soft Bell", + "Xylophone", + "Vibraphone", + "Brass", + "Bass Guitar", + "Synth", + "Chorus", + "Drums" + } }; const char* oplWaveforms[8]={ @@ -1572,10 +1641,59 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##LLPreset",opllInsNames[ins->fm.opllPreset])) { - for (int i=0; i<17; i++) { - if (ImGui::Selectable(opllInsNames[i])) { - ins->fm.opllPreset=i; + + bool isPresent[4]; + int isPresentCount=0; + memset(isPresent,0,4*sizeof(bool)); + for (int i=0; isong.systemLen; i++) { + if (e->song.system[i]==DIV_SYSTEM_VRC7) { + isPresent[3]=true; + } else if (e->song.system[i]==DIV_SYSTEM_OPLL || e->song.system[i]==DIV_SYSTEM_OPLL_DRUMS) { + isPresent[(e->song.systemFlags[i]>>4)&3]=true; + } + } + if (!isPresent[0] && !isPresent[1] && !isPresent[2] && !isPresent[3]) { + isPresent[0]=true; + } + for (int i=0; i<4; i++) { + if (isPresent[i]) isPresentCount++; + } + int presentWhich=0; + for (int i=0; i<4; i++) { + if (isPresent[i]) { + presentWhich=i; + break; + } + } + + if (ImGui::BeginCombo("##LLPreset",opllInsNames[presentWhich][ins->fm.opllPreset])) { + if (isPresentCount>1) { + if (ImGui::BeginTable("LLPresetList",isPresentCount)) { + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + for (int i=0; i<4; i++) { + if (!isPresent[i]) continue; + ImGui::TableNextColumn(); + ImGui::Text("%s name",opllVariants[i]); + } + for (int i=0; i<17; i++) { + ImGui::TableNextRow(); + for (int j=0; j<4; j++) { + if (!isPresent[j]) continue; + ImGui::TableNextColumn(); + ImGui::PushID(j*17+i); + if (ImGui::Selectable(opllInsNames[j][i])) { + ins->fm.opllPreset=i; + } + ImGui::PopID(); + } + } + ImGui::EndTable(); + } + } else { + for (int i=0; i<17; i++) { + if (ImGui::Selectable(opllInsNames[presentWhich][i])) { + ins->fm.opllPreset=i; + } } } ImGui::EndCombo(); From d204ae868a66d288128ed80f76f5eeaabd8f496a Mon Sep 17 00:00:00 2001 From: brickblock369 <59150779+brickblock369@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:03:08 +0900 Subject: [PATCH 12/38] Tweaked versions of the instruments These were originally made by me, but I tweaked them a little. --- instruments/FM/guitar/Acoustic Nylon Guitar.dmp | Bin 51 -> 51 bytes instruments/FM/guitar/Acoustic Steel Guitar.dmp | Bin 51 -> 51 bytes .../FM/guitar/Electric Guitar Harmonics.dmp | Bin 51 -> 51 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/instruments/FM/guitar/Acoustic Nylon Guitar.dmp b/instruments/FM/guitar/Acoustic Nylon Guitar.dmp index b79addcf1c60e804b0f268207fc6e4330fcb786e..35fac263c8a2cec190cd2aedd95271200427ca98 100644 GIT binary patch literal 51 wcmd;PVq{=oV&Kq~l;P)RU|<7s)a2Rufr3CLtA;!WKMR=2$RN+f&k1G%05H%2xBvhE literal 51 wcmd;PVq{=oV&E{6l;P)RU|fr3CLtA;!SKMR=2$RN+f&k1G%052T@pa1{> diff --git a/instruments/FM/guitar/Acoustic Steel Guitar.dmp b/instruments/FM/guitar/Acoustic Steel Guitar.dmp index 64e77b9c8b089a462abbbd0a8875b1406e6cfc0e..295a29d6e38c3e6a54124bdd7f23fb88bfe2b56b 100644 GIT binary patch literal 51 wcmd;PVq{=oV&G7hl;P)RU|?flVAPOjnamRM?EEYcQ3iP~eoinG04;O@m;e9( literal 51 wcmd;PVq{=oV&G7hl;P)RU|?ooVAPOjnamRMEc`4GQ3iP~eoinG04z}fi2wiq diff --git a/instruments/FM/guitar/Electric Guitar Harmonics.dmp b/instruments/FM/guitar/Electric Guitar Harmonics.dmp index 07f2103287a0c7b93f7bfbb4c403bf4c69ee084e..14a14b1184c4b47395c645ad75f3607714bbe17e 100644 GIT binary patch literal 51 ycmW;CF%AGA2mrAIi5eMT(vkT8Pnx)OsH-p(%{md#!FJ&S4dyMvK37%`|KR~B?g5Da literal 51 ycmd;PVq{=vVqg-G7w6|^U|?ooU=fpN;ARBU3=ABK@(lby8i<$} Date: Wed, 3 Aug 2022 00:05:58 -0500 Subject: [PATCH 13/38] SoundUnit: add 64K chip revision --- src/engine/platform/sound/su.cpp | 16 ++++++++-------- src/engine/platform/sound/su.h | 7 +++---- src/engine/platform/su.cpp | 11 ++++++++--- src/engine/platform/su.h | 1 + src/gui/sysConf.cpp | 17 +++++++++++++++++ 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index b7fa731a6..0dd6dcdc7 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -54,7 +54,7 @@ void SoundUnit::NextSample(short* l, short* r) { chan[i].pcmpos=chan[i].pcmrst; } } - chan[i].pcmpos&=(SOUNDCHIP_PCM_SIZE-1); + chan[i].pcmpos&=(pcmSize-1); } else if (chan[i].flags.pcmloop) { chan[i].pcmpos=chan[i].pcmrst; } @@ -228,9 +228,10 @@ void SoundUnit::NextSample(short* l, short* r) { *r=minval(32767,maxval(-32767,tnsR)); } -void SoundUnit::Init() { +void SoundUnit::Init(int sampleMemSize) { + pcmSize=sampleMemSize; Reset(); - memset(pcm,0,SOUNDCHIP_PCM_SIZE); + memset(pcm,0,pcmSize); for (int i=0; i<256; i++) { SCsine[i]=sin((i/128.0f)*M_PI)*127; SCtriangle[i]=(i>127)?(255-i):(i); @@ -242,9 +243,6 @@ void SoundUnit::Init() { SCpantabR[128+i]=i-1; } SCpantabR[128]=0; - for (int i=0; i<8; i++) { - muted[i]=false; - } } void SoundUnit::Reset() { @@ -282,6 +280,8 @@ void SoundUnit::Write(unsigned char addr, unsigned char data) { } SoundUnit::SoundUnit() { - Init(); - memset(pcm,0,SOUNDCHIP_PCM_SIZE); + Init(65536); // default + for (int i=0; i<8; i++) { + muted[i]=false; + } } diff --git a/src/engine/platform/sound/su.h b/src/engine/platform/sound/su.h index 3152e8568..3b125f030 100644 --- a/src/engine/platform/sound/su.h +++ b/src/engine/platform/sound/su.h @@ -3,8 +3,6 @@ #include #include -#define SOUNDCHIP_PCM_SIZE 8192 - class SoundUnit { signed char SCsine[256]; signed char SCtriangle[256]; @@ -24,6 +22,7 @@ class SoundUnit { int tnsL, tnsR; unsigned short oldfreq[8]; unsigned short oldflags[8]; + unsigned int pcmSize; public: unsigned short resetfreq[8]; unsigned short voldcycles[8]; @@ -84,7 +83,7 @@ class SoundUnit { unsigned short wc; unsigned short restimer; } chan[8]; - signed char pcm[SOUNDCHIP_PCM_SIZE]; + signed char pcm[65536]; bool muted[8]; void Write(unsigned char addr, unsigned char data); void NextSample(short* l, short* r); @@ -94,7 +93,7 @@ class SoundUnit { if (ret>32767) ret=32767; return ret; } - void Init(); + void Init(int sampleMemSize=8192); void Reset(); SoundUnit(); }; diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index c2cd6b25e..2d1a38932 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -555,6 +555,11 @@ void DivPlatformSoundUnit::setFlags(unsigned int flags) { for (int i=0; i<8; i++) { oscBuf[i]->rate=rate; } + + sampleMemSize=flags&16; + + su->Init(sampleMemSize?65536:8192); + renderSamples(); } void DivPlatformSoundUnit::poke(unsigned int addr, unsigned short val) { @@ -570,7 +575,7 @@ const void* DivPlatformSoundUnit::getSampleMem(int index) { } size_t DivPlatformSoundUnit::getSampleMemCapacity(int index) { - return (index==0)?8192:0; + return (index==0)?(sampleMemSize?65536:8192):0; } size_t DivPlatformSoundUnit::getSampleMemUsage(int index) { @@ -583,6 +588,7 @@ void DivPlatformSoundUnit::renderSamples() { size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (s->data8==NULL) continue; int paddedLen=s->samples; if (memPos>=getSampleMemCapacity(0)) { logW("out of PCM memory for sample %d!",i); @@ -609,9 +615,8 @@ int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, unsigned isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; } - setFlags(flags); su=new SoundUnit(); - su->Init(); + setFlags(flags); reset(); return 6; } diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 1d39854f2..e882c398e 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -96,6 +96,7 @@ class DivPlatformSoundUnit: public DivDispatch { }; std::queue writes; unsigned char lastPan; + bool sampleMemSize; int cycles, curChan, delay; short tempL; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 365312424..7c624f309 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -120,6 +120,23 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool } break; } + case DIV_SYSTEM_SOUND_UNIT: { + ImGui::Text("CPU rate:"); + if (ImGui::RadioButton("6.18MHz (NTSC)",(flags&15)==0)) { + copyOfFlags=(flags&(~15))|0; + } + if (ImGui::RadioButton("5.95MHz (PAL)",(flags&15)==1)) { + copyOfFlags=(flags&(~15))|1; + } + ImGui::Text("Chip revision (sample memory):"); + if (ImGui::RadioButton("A/B/E (8K)",(flags&16)==0)) { + copyOfFlags=(flags&(~16))|0; + } + if (ImGui::RadioButton("D/F (64K)",(flags&16)==16)) { + copyOfFlags=(flags&(~16))|16; + } + break; + } case DIV_SYSTEM_GB: { bool antiClick=flags&8; if (ImGui::Checkbox("Disable anti-click",&antiClick)) { From 034b4fd4f607f96e08c849b296053bb6b7a9baf5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 00:10:32 -0500 Subject: [PATCH 14/38] GUI: YMF281 patch names thanks nicco1690! --- src/gui/insEdit.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 09d6f4066..08b9fc577 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -70,24 +70,24 @@ const char* opllInsNames[4][17]={ "Electric Guitar", "Drums" }, - // help me get the names! + /* YMF281 */ { "User", - "Name under Register-Wall #1", - "Name under Register-Wall #2", - "Piano", - "Flute", + "Electric String", + "Bow wow", + "Electric Guitar", + "Organ", "Clarinet", - "Name under Register-Wall #6", + "Saxophone", "Trumpet", - "Name under Register-Wall #8", - "Name under Register-Wall #9", - "Name under Register-Wall #10", - "Name under Register-Wall #11", - "Name under Register-Wall #12", - "Name under Register-Wall #13", - "Name under Register-Wall #14", - "Name under Register-Wall #15", + "Street Organ", + "Synth Brass", + "Electric Piano", + "Bass", + "Vibraphone", + "Chime", + "Tom Tom II", + "Noise", "Drums" }, // help me get the names! From 89042f61eb57e14c6b1f966f47088e2dd0f7dc31 Mon Sep 17 00:00:00 2001 From: cam900 Date: Wed, 3 Aug 2022 21:56:22 +0900 Subject: [PATCH 15/38] Fix link for vgsound_emu (moved into https://gitlab.com/cam900/vgsound_emu) --- src/engine/platform/sound/k005289/k005289.cpp | 2 +- src/engine/platform/sound/k005289/k005289.hpp | 2 +- src/engine/platform/sound/n163/n163.cpp | 2 +- src/engine/platform/sound/n163/n163.hpp | 2 +- src/engine/platform/sound/oki/msm6295.cpp | 2 +- src/engine/platform/sound/oki/msm6295.hpp | 2 +- src/engine/platform/sound/oki/util.hpp | 2 +- src/engine/platform/sound/oki/vox.hpp | 2 +- src/engine/platform/sound/scc/scc.cpp | 2 +- src/engine/platform/sound/scc/scc.hpp | 2 +- src/engine/platform/sound/vrcvi/vrcvi.cpp | 2 +- src/engine/platform/sound/vrcvi/vrcvi.hpp | 2 +- src/engine/platform/sound/x1_010/x1_010.cpp | 2 +- src/engine/platform/sound/x1_010/x1_010.hpp | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/engine/platform/sound/k005289/k005289.cpp b/src/engine/platform/sound/k005289/k005289.cpp index 8b21245a1..c9bdf83ae 100644 --- a/src/engine/platform/sound/k005289/k005289.cpp +++ b/src/engine/platform/sound/k005289/k005289.cpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: cam900 diff --git a/src/engine/platform/sound/k005289/k005289.hpp b/src/engine/platform/sound/k005289/k005289.hpp index d042e1d14..575a98b87 100644 --- a/src/engine/platform/sound/k005289/k005289.hpp +++ b/src/engine/platform/sound/k005289/k005289.hpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: cam900 diff --git a/src/engine/platform/sound/n163/n163.cpp b/src/engine/platform/sound/n163/n163.cpp index 5d9deab1a..3fd30bc44 100644 --- a/src/engine/platform/sound/n163/n163.cpp +++ b/src/engine/platform/sound/n163/n163.cpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: cam900, tildearrow diff --git a/src/engine/platform/sound/n163/n163.hpp b/src/engine/platform/sound/n163/n163.hpp index 800d1ea13..317a33b45 100644 --- a/src/engine/platform/sound/n163/n163.hpp +++ b/src/engine/platform/sound/n163/n163.hpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: cam900, tildearrow diff --git a/src/engine/platform/sound/oki/msm6295.cpp b/src/engine/platform/sound/oki/msm6295.cpp index 1b7fe568a..e7f39d27e 100644 --- a/src/engine/platform/sound/oki/msm6295.cpp +++ b/src/engine/platform/sound/oki/msm6295.cpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: tildearrow diff --git a/src/engine/platform/sound/oki/msm6295.hpp b/src/engine/platform/sound/oki/msm6295.hpp index 5203f4340..ca8b81d41 100644 --- a/src/engine/platform/sound/oki/msm6295.hpp +++ b/src/engine/platform/sound/oki/msm6295.hpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: tildearrow diff --git a/src/engine/platform/sound/oki/util.hpp b/src/engine/platform/sound/oki/util.hpp index 731772acc..b9c50d7bf 100644 --- a/src/engine/platform/sound/oki/util.hpp +++ b/src/engine/platform/sound/oki/util.hpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: tildearrow diff --git a/src/engine/platform/sound/oki/vox.hpp b/src/engine/platform/sound/oki/vox.hpp index c085c0b7c..23fbfd78e 100644 --- a/src/engine/platform/sound/oki/vox.hpp +++ b/src/engine/platform/sound/oki/vox.hpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: tildearrow diff --git a/src/engine/platform/sound/scc/scc.cpp b/src/engine/platform/sound/scc/scc.cpp index 3e230447a..e2ebcf200 100644 --- a/src/engine/platform/sound/scc/scc.cpp +++ b/src/engine/platform/sound/scc/scc.cpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + 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 diff --git a/src/engine/platform/sound/scc/scc.hpp b/src/engine/platform/sound/scc/scc.hpp index db99ec877..24a365ccf 100644 --- a/src/engine/platform/sound/scc/scc.hpp +++ b/src/engine/platform/sound/scc/scc.hpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + 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 diff --git a/src/engine/platform/sound/vrcvi/vrcvi.cpp b/src/engine/platform/sound/vrcvi/vrcvi.cpp index 87ff05d7c..a811c2f44 100644 --- a/src/engine/platform/sound/vrcvi/vrcvi.cpp +++ b/src/engine/platform/sound/vrcvi/vrcvi.cpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: cam900, tildearrow diff --git a/src/engine/platform/sound/vrcvi/vrcvi.hpp b/src/engine/platform/sound/vrcvi/vrcvi.hpp index 790061c82..4a80f7577 100644 --- a/src/engine/platform/sound/vrcvi/vrcvi.hpp +++ b/src/engine/platform/sound/vrcvi/vrcvi.hpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: cam900, tildearrow diff --git a/src/engine/platform/sound/x1_010/x1_010.cpp b/src/engine/platform/sound/x1_010/x1_010.cpp index c047b854a..6b0041bad 100644 --- a/src/engine/platform/sound/x1_010/x1_010.cpp +++ b/src/engine/platform/sound/x1_010/x1_010.cpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: cam900, tildearrow diff --git a/src/engine/platform/sound/x1_010/x1_010.hpp b/src/engine/platform/sound/x1_010/x1_010.hpp index 3f5d9d4e2..b533b66d8 100644 --- a/src/engine/platform/sound/x1_010/x1_010.hpp +++ b/src/engine/platform/sound/x1_010/x1_010.hpp @@ -1,6 +1,6 @@ /* License: BSD-3-Clause - see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details + see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details Copyright holder(s): cam900 Modifiers and Contributors for Furnace: cam900, tildearrow From 0183c5d9ff134700d65c68d4193986c0f36fe429 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 01:13:59 -0500 Subject: [PATCH 16/38] GUI: remove one new line --- src/gui/insEdit.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 08b9fc577..958f662d4 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -70,7 +70,6 @@ const char* opllInsNames[4][17]={ "Electric Guitar", "Drums" }, - /* YMF281 */ { "User", "Electric String", From 46425655ad548ad6cfe2f31cfdaa455a8efad7c7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 14:38:39 -0500 Subject: [PATCH 17/38] YM2612: fix possible ExtCh DualPCM muting issue --- src/engine/platform/genesisext.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index bfe771a6d..1f320dd34 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -24,6 +24,8 @@ #define CHIP_FREQBASE fmFreqBase #define CHIP_DIVIDER fmDivBase +#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6]))) + int DivPlatformGenesisExt::dispatch(DivCommand c) { if (c.chan<2) { return DivPlatformGenesis::dispatch(c); @@ -542,7 +544,7 @@ void DivPlatformGenesisExt::forceIns() { rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); if (chan[i].active) { chan[i].keyOn=true; chan[i].freqChanged=true; From 53120edd9985a1a633b5b0e052cced711c254f89 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 14:41:23 -0500 Subject: [PATCH 18/38] disable MIDI clock --- 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 662e2ddb4..dd58b0d1a 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -914,7 +914,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { // MIDI clock if (output) if (!skipping && output->midiOut!=NULL) { - output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0)); + //output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0)); } while (!pendingNotes.empty()) { From 52c3b1037392ea92ef55236f09fc73aae5697d0c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 16:21:30 -0500 Subject: [PATCH 19/38] add getWantPreNote() currently only C64 system requires this --- src/engine/dispatch.h | 6 ++++++ src/engine/platform/abstract.cpp | 4 ++++ src/engine/platform/c64.cpp | 4 ++++ src/engine/platform/c64.h | 1 + src/engine/playback.cpp | 4 +++- 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 03bc23ca6..f1418e481 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -399,6 +399,12 @@ class DivDispatch { */ virtual bool getDCOffRequired(); + /** + * check whether PRE_NOTE command is desired. + * @return truth. + */ + virtual bool getWantPreNote(); + /** * get a description of a dispatch-specific effect. * @param effect the effect. diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 54366e1ba..5b732e7e9 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -90,6 +90,10 @@ bool DivDispatch::getDCOffRequired() { return false; } +bool DivDispatch::getWantPreNote() { + return false; +} + const char* DivDispatch::getEffectName(unsigned char effect) { return NULL; } diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 640df54e6..9049ffec4 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -513,6 +513,10 @@ bool DivPlatformC64::getDCOffRequired() { return true; } +bool DivPlatformC64::getWantPreNote() { + return true; +} + void DivPlatformC64::reset() { for (int i=0; i<3; i++) { chan[i]=DivPlatformC64::Channel(); diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index d9dc08040..ae1034a82 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -97,6 +97,7 @@ class DivPlatformC64: public DivDispatch { void setFlags(unsigned int flags); void notifyInsChange(int ins); bool getDCOffRequired(); + bool getWantPreNote(); DivMacroInt* getChanMacroInt(int ch); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index dd58b0d1a..6c4376573 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -866,7 +866,9 @@ void DivEngine::nextRow() { if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) { if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) { if (!chan[i].legato) { - dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks)); + if (disCont[dispatchOfChan[i]].dispatch!=NULL) { + if (disCont[dispatchOfChan[i]].dispatch->getWantPreNote()) dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks)); + } if (song.oneTickCut) { bool doPrepareCut=true; From fce03717563eaf4fced702c0684b8af810c47f3a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 17:21:47 -0500 Subject: [PATCH 20/38] add "hint" commands --- src/engine/dispatch.h | 8 ++++++++ src/engine/playback.cpp | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index f1418e481..86b2e229e 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -55,6 +55,14 @@ enum DivDispatchCmds { DIV_CMD_PRE_PORTA, // (inPorta, isPortaOrSlide) DIV_CMD_PRE_NOTE, // used in C64 (note) + // these will be used in ROM export. + // do NOT implement! + DIV_CMD_HINT_VIBRATO, // (speed, depth) + DIV_CMD_HINT_VIBRATO_SHAPE, // (shape) + DIV_CMD_HINT_PITCH, // (pitch) + DIV_CMD_HINT_ARPEGGIO, // (note1, note2) + DIV_CMD_HINT_VOL_SLIDE, // (amount, oneTick) + DIV_CMD_SAMPLE_MODE, // (enabled) DIV_CMD_SAMPLE_FREQ, // (frequency) DIV_CMD_SAMPLE_BANK, // (bank) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 6c4376573..b089b6f3e 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -57,6 +57,12 @@ const char* cmdName[]={ "PRE_PORTA", "PRE_NOTE", + "HINT_VIBRATO", + "HINT_VIBRATO_SHAPE", + "HINT_PITCH", + "HINT_ARPEGGIO", + "HINT_VOL_SLIDE", + "SAMPLE_MODE", "SAMPLE_FREQ", "SAMPLE_BANK", From eafbf24290567d9ba3f0e0cd6da0c2a5fba112e3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 17:31:00 -0500 Subject: [PATCH 21/38] GUI: YM2423 patch names thanks freq-mod! --- src/gui/insEdit.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 958f662d4..f991b9e2a 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -89,24 +89,23 @@ const char* opllInsNames[4][17]={ "Noise", "Drums" }, - // help me get the names! /* YM2423 */ { "User", - "Violin", - "What is this", - "Some kind of space sound", - "Voice maybe", - "Clarinet", - "Slap something", + "Strings", + "Guitar", + "Electric Guitar", + "Electric Piano", + "Flute", + "Marimba", "Trumpet", - "AAAAAA", - "Wow!", - "Synth", - "Synth Key", + "Harmonica", + "Tuba", + "Synth Brass", + "Short Saw", "Vibraphone", - "Space Bass", + "Electric Guitar 2", "Synth Bass", - "Sound like the default Defle patch", + "Sitar", "Drums" }, // stolen from FamiTracker From 7ec4f7cb9e51c7016a753447d2b92ea7e9e8492a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 18:44:45 -0500 Subject: [PATCH 22/38] VGM export: add option to insert pattern change hi nts --- src/engine/engine.h | 4 +++- src/engine/vgmOps.cpp | 28 +++++++++++++++++++++++++++- src/gui/gui.cpp | 19 ++++++++++++++++++- src/gui/gui.h | 2 +- 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 3030cfadb..8fd56ff91 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -472,7 +472,9 @@ class DivEngine { // specify system to build ROM for. SafeWriter* buildROM(int sys); // dump to VGM. - SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171); + SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false); + // dump command stream. + SafeWriter* saveCommand(bool binary=false); // export to an audio file bool saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime=0.0); // wait for audio export to finish diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index a500b4848..15de0156f 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -802,7 +802,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } } -SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { +SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool patternHints) { if (version<0x150) { lastError="VGM version is too low"; return NULL; @@ -1795,6 +1795,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { playSub(false); size_t tickCount=0; bool writeLoop=false; + int ord=-1; + int exportChans=0; + for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0xfe); + w->writeI(3+exportChans); + w->writeC(0x01); + w->writeC(prevOrder); + w->writeC(prevRow); + for (int i=0; iwriteC(curSubSong->orders.ord[i][prevOrder]); + } + } + } } // get register dumps for (int i=0; isong.systemLen; i++) { @@ -3468,7 +3484,7 @@ bool FurnaceGUI::loop() { } break; case GUI_FILE_EXPORT_VGM: { - SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion); + SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints); if (w!=NULL) { FILE* f=ps_fopen(copyOfName.c_str(),"wb"); if (f!=NULL) { @@ -4431,6 +4447,7 @@ FurnaceGUI::FurnaceGUI(): displayError(false), displayExporting(false), vgmExportLoop(true), + vgmExportPatternHints(false), wantCaptureKeyboard(false), oldWantCaptureKeyboard(false), displayMacroMenu(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 30113953f..cb61d48dc 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -952,7 +952,7 @@ class FurnaceGUI { String mmlString[32]; String mmlStringW; - bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; + bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints, wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly; bool displayPendingIns, pendingInsSingle; bool willExport[32]; From a0d10aa60be74a54c27e48ca7b3ed3d7027947c6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 3 Aug 2022 19:17:18 -0500 Subject: [PATCH 23/38] Game Boy: implement anti-click --- src/engine/platform/gb.cpp | 19 +++++++++++++++++-- src/engine/platform/gb.h | 4 ++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 2b7dcf6df..4d10d7d22 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -97,10 +97,11 @@ void DivPlatformGB::acquire(short* bufL, short* bufR, size_t start, size_t len) void DivPlatformGB::updateWave() { rWrite(0x1a,0); for (int i=0; i<16; i++) { - int nibble1=15-ws.output[i<<1]; - int nibble2=15-ws.output[1+(i<<1)]; + int nibble1=15-ws.output[((i<<1)+antiClickWavePos)&31]; + int nibble2=15-ws.output[((1+(i<<1))+antiClickWavePos)&31]; rWrite(0x30+i,(nibble1<<4)|nibble2); } + antiClickWavePos&=31; } static unsigned char chanMuteMask[4]={ @@ -151,6 +152,12 @@ static unsigned char noiseTable[256]={ }; void DivPlatformGB::tick(bool sysTick) { + if (antiClickEnabled && sysTick && chan[2].freq>0) { + antiClickPeriodCount+=((chipClock>>1)/MAX(parent->getCurHz(),1.0f)); + antiClickWavePos+=antiClickPeriodCount/chan[2].freq; + antiClickPeriodCount%=chan[2].freq; + } + for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.arp.had) { @@ -471,6 +478,9 @@ void DivPlatformGB::reset() { lastPan=0xff; immWrite(0x25,procMute()); immWrite(0x24,0x77); + + antiClickPeriodCount=0; + antiClickWavePos=0; } bool DivPlatformGB::isStereo() { @@ -507,6 +517,10 @@ void DivPlatformGB::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) immWrite(i.addr,i.val); } +void DivPlatformGB::setFlags(unsigned int flags) { + antiClickEnabled=!(flags&8); +} + int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { chipClock=4194304; rate=chipClock/16; @@ -519,6 +533,7 @@ int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, unsigned int fl dumpWrites=false; skipRegisterWrites=false; gb=new GB_gameboy_t; + setFlags(flags); reset(); return 4; } diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index fe2f6e51b..b00458c2d 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -59,9 +59,12 @@ class DivPlatformGB: public DivDispatch { Channel chan[4]; DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; + bool antiClickEnabled; unsigned char lastPan; DivWaveSynth ws; + int antiClickPeriodCount, antiClickWavePos; + GB_gameboy_t* gb; unsigned char regPool[128]; @@ -88,6 +91,7 @@ class DivPlatformGB: public DivDispatch { void poke(std::vector& wlist); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); + void setFlags(unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); ~DivPlatformGB(); From d54d853ff83c14994151619b41f696a777b909e5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Aug 2022 00:51:47 -0500 Subject: [PATCH 24/38] add a command stream dump option --- src/engine/engine.cpp | 106 ++++++++++++++++++++++++++++++++++++++ src/engine/engine.h | 2 + src/engine/safeWriter.cpp | 3 ++ src/engine/safeWriter.h | 1 + src/gui/gui.cpp | 65 ++++++++++++++++++++++- src/gui/gui.h | 8 ++- 6 files changed, 182 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 2a000476d..9b532c0bf 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "dispatch.h" #define _USE_MATH_DEFINES #include "engine.h" #include "instrument.h" @@ -234,6 +235,111 @@ double DivEngine::benchmarkSeek() { return tAvg; } +SafeWriter* DivEngine::saveCommand(bool binary) { + logI("implement! %d",binary); + stop(); + repeatPattern=false; + setOrder(0); + BUSY_BEGIN_SOFT; + // determine loop point + int loopOrder=0; + int loopRow=0; + int loopEnd=0; + walkSong(loopOrder,loopRow,loopEnd); + logI("loop point: %d %d",loopOrder,loopRow); + + SafeWriter* w=new SafeWriter; + w->init(); + + // write header + w->writeText("# Furnace Command Stream\n\n"); + + w->writeText("[Information]\n"); + w->writeText(fmt::sprintf("name: %s\n",song.name)); + w->writeText(fmt::sprintf("author: %s\n",song.author)); + w->writeText(fmt::sprintf("category: %s\n",song.category)); + w->writeText(fmt::sprintf("system: %s\n",song.systemName)); + + w->writeText("\n"); + + w->writeText("[SubSongInformation]\n"); + w->writeText(fmt::sprintf("name: %s\n",curSubSong->name)); + w->writeText(fmt::sprintf("tickRate: %f\n",curSubSong->hz)); + + w->writeText("\n"); + + w->writeText("[SysDefinition]\n"); + // TODO + + w->writeText("\n"); + + // play the song ourselves + bool done=false; + playSub(false); + + w->writeText("[Stream]\n"); + int tick=0; + bool oldCmdStreamEnabled=cmdStreamEnabled; + cmdStreamEnabled=true; + double curDivider=divider; + while (!done) { + if (nextTick(false,true) || !playing) { + done=true; + } + // get command stream + bool wroteTick=false; + if (curDivider!=divider) { + curDivider=divider; + if (!wroteTick) { + wroteTick=true; + w->writeText(fmt::sprintf(">> TICK %d\n",tick)); + } + w->writeText(fmt::sprintf(">> SET_RATE %f\n",curDivider)); + } + for (DivCommand& i: cmdStream) { + switch (i.cmd) { + // strip away hinted/useless commands + case DIV_ALWAYS_SET_VOLUME: + break; + case DIV_CMD_GET_VOLUME: + break; + case DIV_CMD_VOLUME: + break; + case DIV_CMD_NOTE_PORTA: + break; + case DIV_CMD_LEGATO: + break; + case DIV_CMD_PITCH: + break; + default: + if (!wroteTick) { + wroteTick=true; + w->writeText(fmt::sprintf(">> TICK %d\n",tick)); + } + w->writeText(fmt::sprintf(" %d: %s %d %d\n",i.chan,cmdName[i.cmd],i.value,i.value2)); + break; + } + } + cmdStream.clear(); + tick++; + } + cmdStreamEnabled=oldCmdStreamEnabled; + + if (!playing) { + w->writeText(">> END\n"); + } else { + w->writeText(">> LOOP 0\n"); + } + + remainingLoops=-1; + playing=false; + freelance=false; + extValuePresent=false; + BUSY_END; + + return w; +} + void _runExportThread(DivEngine* caller) { caller->runExportThread(); } diff --git a/src/engine/engine.h b/src/engine/engine.h index 8fd56ff91..6f969efc9 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -280,6 +280,8 @@ enum DivChanTypes { DIV_CH_OP=5 }; +extern const char* cmdName[]; + class DivEngine { DivDispatchContainer disCont[32]; TAAudio* output; diff --git a/src/engine/safeWriter.cpp b/src/engine/safeWriter.cpp index f29800a4a..e61380936 100644 --- a/src/engine/safeWriter.cpp +++ b/src/engine/safeWriter.cpp @@ -120,6 +120,9 @@ int SafeWriter::writeWString(WString val, bool pascal) { return 2+val.size()*2; } } +int SafeWriter::writeText(String val) { + return write(val.c_str(),val.size()); +} void SafeWriter::init() { if (operative) return; diff --git a/src/engine/safeWriter.h b/src/engine/safeWriter.h index 9072c61d7..414417fd2 100644 --- a/src/engine/safeWriter.h +++ b/src/engine/safeWriter.h @@ -57,6 +57,7 @@ class SafeWriter { int writeD_BE(double val); int writeWString(WString val, bool pascal); int writeString(String val, bool pascal); + int writeText(String val); void init(); SafeReader* toReader(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index c296ab2c3..28edd9e32 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1401,6 +1401,17 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { dpiScale ); break; + case GUI_FILE_EXPORT_CMDSTREAM: + if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir(); + hasOpened=fileDialog->openSave( + "Export Command Stream", + {"text file", "*.txt", + "binary file", "*.bin"}, + "text file{.txt},binary file{.bin}", + workingDirROMExport, + dpiScale + ); + break; case GUI_FILE_EXPORT_ROM: showError("Coming soon!"); break; @@ -2947,6 +2958,19 @@ bool FurnaceGUI::loop() { } ImGui::EndMenu(); } + if (ImGui::BeginMenu("export command stream...")) { + ImGui::Text( + "this option exports a text or binary file which\n" + "contains a dump of the internal command stream\n" + "produced when playing the song.\n\n" + + "technical/development use only!" + ); + if (ImGui::Button("export")) { + openFileDialog(GUI_FILE_EXPORT_CMDSTREAM); + } + ImGui::EndMenu(); + } ImGui::Separator(); if (ImGui::BeginMenu("add system...")) { for (int j=0; availableSystems[j]; j++) { @@ -3258,9 +3282,12 @@ bool FurnaceGUI::loop() { workingDirAudioExport=fileDialog->getPath()+DIR_SEPARATOR_STR; break; case GUI_FILE_EXPORT_VGM: - case GUI_FILE_EXPORT_ROM: workingDirVGMExport=fileDialog->getPath()+DIR_SEPARATOR_STR; break; + case GUI_FILE_EXPORT_ROM: + case GUI_FILE_EXPORT_CMDSTREAM: + workingDirROMExport=fileDialog->getPath()+DIR_SEPARATOR_STR; + break; case GUI_FILE_LOAD_MAIN_FONT: case GUI_FILE_LOAD_PAT_FONT: workingDirFont=fileDialog->getPath()+DIR_SEPARATOR_STR; @@ -3325,6 +3352,11 @@ bool FurnaceGUI::loop() { if (curFileDialog==GUI_FILE_EXPORT_VGM) { checkExtension(".vgm"); } + if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM) { + // we can't tell whether the user chose .txt or .bin in the system file picker + const char* fallbackExt=(settings.sysFileDialog || ImGuiFileDialog::Instance()->GetCurrentFilter()=="text file")?".txt":".bin"; + checkExtensionDual(".txt",".bin",fallbackExt); + } if (curFileDialog==GUI_FILE_EXPORT_COLORS) { checkExtension(".cfgc"); } @@ -3506,6 +3538,35 @@ bool FurnaceGUI::loop() { case GUI_FILE_EXPORT_ROM: showError("Coming soon!"); break; + case GUI_FILE_EXPORT_CMDSTREAM: { + String lowerCase=fileName; + for (char& i: lowerCase) { + if (i>='A' && i<='Z') i+='a'-'A'; + } + bool isBinary=true; + if ((lowerCase.size()<4 || lowerCase.rfind(".txt")!=lowerCase.size()-4)) { + isBinary=false; + } + + SafeWriter* w=e->saveCommand(isBinary); + if (w!=NULL) { + FILE* f=ps_fopen(copyOfName.c_str(),"wb"); + if (f!=NULL) { + fwrite(w->getFinalBuf(),1,w->size(),f); + fclose(f); + } else { + showError("could not open file!"); + } + w->finish(); + delete w; + if (!e->getWarnings().empty()) { + showWarning(e->getWarnings(),GUI_WARN_GENERIC); + } + } else { + showError(fmt::sprintf("could not write command stream! (%s)",e->getLastError())); + } + break; + } case GUI_FILE_LOAD_MAIN_FONT: settings.mainFontPath=copyOfName; break; @@ -4099,6 +4160,7 @@ bool FurnaceGUI::init() { workingDirSample=e->getConfString("lastDirSample",workingDir); workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir); workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir); + workingDirROMExport=e->getConfString("lastDirROMExport",workingDir); workingDirFont=e->getConfString("lastDirFont",workingDir); workingDirColors=e->getConfString("lastDirColors",workingDir); workingDirKeybinds=e->getConfString("lastDirKeybinds",workingDir); @@ -4339,6 +4401,7 @@ bool FurnaceGUI::finish() { e->setConf("lastDirSample",workingDirSample); e->setConf("lastDirAudioExport",workingDirAudioExport); e->setConf("lastDirVGMExport",workingDirVGMExport); + e->setConf("lastDirROMExport",workingDirROMExport); e->setConf("lastDirFont",workingDirFont); e->setConf("lastDirColors",workingDirColors); e->setConf("lastDirKeybinds",workingDirKeybinds); diff --git a/src/gui/gui.h b/src/gui/gui.h index cb61d48dc..0ce4afaee 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -266,6 +266,7 @@ enum FurnaceGUIFileDialogs { GUI_FILE_EXPORT_AUDIO_PER_SYS, GUI_FILE_EXPORT_AUDIO_PER_CHANNEL, GUI_FILE_EXPORT_VGM, + GUI_FILE_EXPORT_CMDSTREAM, GUI_FILE_EXPORT_ROM, GUI_FILE_LOAD_MAIN_FONT, GUI_FILE_LOAD_PAT_FONT, @@ -948,11 +949,14 @@ class FurnaceGUI { bool updateSampleTex; String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile; - String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout, workingDirROM, workingDirTest; + String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport; + String workingDirVGMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds; + String workingDirLayout, workingDirROM, workingDirTest; String mmlString[32]; String mmlStringW; - bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints, wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; + bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints; + bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly; bool displayPendingIns, pendingInsSingle; bool willExport[32]; From 09e32c7050fba4ccd4c0b2a2dc82b66c28fb5d14 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Aug 2022 15:14:29 -0500 Subject: [PATCH 25/38] finish command dump hints --- src/engine/dispatch.h | 4 ++++ src/engine/playback.cpp | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 86b2e229e..fb9be5800 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -58,10 +58,14 @@ enum DivDispatchCmds { // these will be used in ROM export. // do NOT implement! DIV_CMD_HINT_VIBRATO, // (speed, depth) + DIV_CMD_HINT_VIBRATO_RANGE, // (range) DIV_CMD_HINT_VIBRATO_SHAPE, // (shape) DIV_CMD_HINT_PITCH, // (pitch) DIV_CMD_HINT_ARPEGGIO, // (note1, note2) + DIV_CMD_HINT_VOLUME, // (vol) DIV_CMD_HINT_VOL_SLIDE, // (amount, oneTick) + DIV_CMD_HINT_PORTA, // (target, speed) + DIV_CMD_HINT_LEGATO, // (note) DIV_CMD_SAMPLE_MODE, // (enabled) DIV_CMD_SAMPLE_FREQ, // (frequency) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index b089b6f3e..8fd20d5f9 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -58,10 +58,14 @@ const char* cmdName[]={ "PRE_NOTE", "HINT_VIBRATO", + "HINT_VIBRATO_RANGE", "HINT_VIBRATO_SHAPE", "HINT_PITCH", "HINT_ARPEGGIO", "HINT_VOL_SLIDE", + "HINT_VOLUME", + "HINT_PORTA", + "HINT_LEGATO", "SAMPLE_MODE", "SAMPLE_FREQ", @@ -344,6 +348,7 @@ void DivEngine::processRow(int i, bool afterDelay) { logV("forcing volume"); chan[i].volume=chan[i].volMax; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } } } @@ -356,11 +361,13 @@ void DivEngine::processRow(int i, bool afterDelay) { if (chan[i].stopOnOff) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].stopOnOff=false; } if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { chan[i+1].portaNote=-1; chan[i+1].portaSpeed=-1; @@ -377,11 +384,13 @@ void DivEngine::processRow(int i, bool afterDelay) { if (chan[i].stopOnOff) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].stopOnOff=false; } if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { chan[i+1].portaNote=-1; chan[i+1].portaSpeed=-1; @@ -398,6 +407,7 @@ void DivEngine::processRow(int i, bool afterDelay) { if (!chan[i].keyOn) { if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsArp(dispatchChanOfChan[i])) { chan[i].arp=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp)); } } chan[i].doNote=true; @@ -414,6 +424,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } chan[i].volume=pat->data[whatRow][3]<<8; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } } @@ -458,11 +469,13 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].inPorta=false; if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { chan[i].portaNote=song.limitSlides?0x60:255; chan[i].portaSpeed=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; chan[i].nowYouCanStop=false; chan[i].stopOnOff=false; @@ -478,11 +491,13 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].inPorta=false; if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60; chan[i].portaSpeed=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; chan[i].nowYouCanStop=false; chan[i].stopOnOff=false; @@ -496,6 +511,7 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].inPorta=false; dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { @@ -509,6 +525,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].inPorta=true; chan[i].wasShorthandPorta=false; } + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; if (chan[i].keyOn) chan[i].doNote=false; chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! @@ -520,6 +537,7 @@ void DivEngine::processRow(int i, bool afterDelay) { case 0x04: // vibrato chan[i].vibratoDepth=effectVal&15; chan[i].vibratoRate=effectVal>>4; + dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO,i,chan[i].vibratoDepth,chan[i].vibratoRate)); dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); break; case 0x07: // tremolo @@ -543,12 +561,14 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x00: // arpeggio chan[i].arp=effectVal; if (chan[i].arp==0 && song.arp0Reset) { chan[i].resetArp=true; } + dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp)); break; case 0x0c: // retrigger if (effectVal!=0) { @@ -580,6 +600,7 @@ void DivEngine::processRow(int i, bool afterDelay) { case 0xe1: // portamento up chan[i].portaNote=chan[i].note+(effectVal&15); chan[i].portaSpeed=(effectVal>>4)*4; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; chan[i].nowYouCanStop=false; chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! @@ -598,6 +619,7 @@ void DivEngine::processRow(int i, bool afterDelay) { case 0xe2: // portamento down chan[i].portaNote=chan[i].note-(effectVal&15); chan[i].portaSpeed=(effectVal>>4)*4; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].portaStop=true; chan[i].nowYouCanStop=false; chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! @@ -615,9 +637,11 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xe3: // vibrato direction chan[i].vibratoDir=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO_SHAPE,i,chan[i].vibratoDir)); break; case 0xe4: // vibrato fine chan[i].vibratoFine=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO_RANGE,i,chan[i].vibratoFine)); break; case 0xe5: // pitch chan[i].pitch=effectVal-0x80; @@ -628,6 +652,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } //chan[i].pitch+=globalPitch; dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); + dispatchCmd(DivCommand(DIV_CMD_HINT_PITCH,i,chan[i].pitch)); break; case 0xea: // legato mode chan[i].legato=effectVal; @@ -677,17 +702,21 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xf3: // fine volume ramp up chan[i].volSpeed=effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf4: // fine volume ramp down chan[i].volSpeed=-effectVal; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf8: // single volume ramp up chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); break; case 0xf9: // single volume ramp down chan[i].volume=MAX(chan[i].volume-effectVal*256,0); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); break; case 0xfa: // fast volume ramp if (effectVal!=0) { @@ -699,6 +728,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xff: // stop song @@ -729,15 +759,18 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); if (chan[i].legato) { dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note)); } else { if (chan[i].inPorta && chan[i].keyOn && !chan[i].shorthandPorta) { if (song.e1e2StopOnSameNote && chan[i].wasShorthandPorta) { chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); chan[i].wasShorthandPorta=false; chan[i].inPorta=false; } else { chan[i].portaNote=chan[i].note; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); } } else if (!chan[i].noteOnInhibit) { dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8)); @@ -748,12 +781,14 @@ void DivEngine::processRow(int i, bool afterDelay) { if (!chan[i].keyOn && chan[i].scheduledSlideReset) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].scheduledSlideReset=false; chan[i].inPorta=false; } if (!chan[i].keyOn && chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } chan[i].keyOn=true; chan[i].keyOff=false; @@ -993,15 +1028,19 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; chan[i].volSpeed=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); } else if (chan[i].volume<0) { chan[i].volSpeed=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); if (song.legacyVolumeSlides) { chan[i].volume=chan[i].volMax+1; } else { chan[i].volume=0; } dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } else { dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } @@ -1030,10 +1069,12 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) { if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) { chan[i].portaSpeed=0; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].oldNote=chan[i].note; chan[i].note=chan[i].portaNote; chan[i].inPorta=false; dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note)); } } } @@ -1047,11 +1088,13 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (chan[i].stopOnOff) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); chan[i].stopOnOff=false; } if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed)); /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { chan[i+1].portaNote=-1; chan[i+1].portaSpeed=-1; @@ -1065,6 +1108,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } if (chan[i].resetArp) { dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note)); chan[i].resetArp=false; } if (song.rowResetsArpPos && firstTick) { From 67e7e07048c30f781e3dce4433aa9d9e1bc985a4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Aug 2022 15:18:48 -0500 Subject: [PATCH 26/38] add -cmdout option --- src/main.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index f25d08e77..6e0173aba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -52,6 +52,7 @@ FurnaceCLI cli; String outName; String vgmOutName; +String cmdOutName; int loops=1; int benchMode=0; DivAudioExportModes outMode=DIV_EXPORT_MODE_ONE; @@ -250,6 +251,12 @@ TAParamResult pVGMOut(String val) { return TA_PARAM_SUCCESS; } +TAParamResult pCmdOut(String val) { + cmdOutName=val; + e.setAudio(DIV_AUDIO_DUMMY); + return TA_PARAM_SUCCESS; +} + bool needsValue(String param) { for (size_t i=0; i","output audio to file")); params.push_back(TAParam("O","vgmout",true,pVGMOut,"","output .vgm data")); + params.push_back(TAParam("C","cmdout",true,pCmdOut,"","output command stream")); params.push_back(TAParam("L","loglevel",true,pLogLevel,"debug|info|warning|error","set the log level (info by default)")); params.push_back(TAParam("v","view",true,pView,"pattern|commands|nothing","set visualization (pattern by default)")); params.push_back(TAParam("c","console",false,pConsole,"","enable console mode")); @@ -307,6 +315,7 @@ int main(int argc, char** argv) { #endif outName=""; vgmOutName=""; + cmdOutName=""; initParams(); @@ -443,7 +452,23 @@ int main(int argc, char** argv) { } return 0; } - if (outName!="" || vgmOutName!="") { + if (outName!="" || vgmOutName!="" || cmdOutName!="") { + if (cmdOutName!="") { + SafeWriter* w=e.saveCommand(false); + if (w!=NULL) { + FILE* f=fopen(cmdOutName.c_str(),"wb"); + if (f!=NULL) { + fwrite(w->getFinalBuf(),1,w->size(),f); + fclose(f); + } else { + reportError(fmt::sprintf("could not open file! (%s)",e.getLastError())); + } + w->finish(); + delete w; + } else { + reportError("could not write command stream!"); + } + } if (vgmOutName!="") { SafeWriter* w=e.saveVGM(); if (w!=NULL) { From 2e41d117d7b259ccb45c89a50602bdfdef368a67 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Aug 2022 17:47:59 -0500 Subject: [PATCH 27/38] fix some of these command hints --- src/engine/engine.cpp | 2 ++ src/engine/playback.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9b532c0bf..5d9224002 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -311,6 +311,8 @@ SafeWriter* DivEngine::saveCommand(bool binary) { break; case DIV_CMD_PITCH: break; + case DIV_CMD_PRE_NOTE: + break; default: if (!wroteTick) { wroteTick=true; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 8fd20d5f9..ee9115fd8 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -62,8 +62,8 @@ const char* cmdName[]={ "HINT_VIBRATO_SHAPE", "HINT_PITCH", "HINT_ARPEGGIO", - "HINT_VOL_SLIDE", "HINT_VOLUME", + "HINT_VOL_SLIDE", "HINT_PORTA", "HINT_LEGATO", @@ -340,8 +340,8 @@ void DivEngine::processRow(int i, bool afterDelay) { // instrument bool insChanged=false; if (pat->data[whatRow][2]!=-1) { - dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[whatRow][2])); if (chan[i].lastIns!=pat->data[whatRow][2]) { + dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[whatRow][2])); chan[i].lastIns=pat->data[whatRow][2]; insChanged=true; if (song.legacyVolumeSlides && chan[i].volume==chan[i].volMax+1) { From f2b6f854a99e12d6a8f026890f85d93d55c7dc7b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Aug 2022 17:48:10 -0500 Subject: [PATCH 28/38] add options to not install demo songs/ins --- CMakeLists.txt | 10 ++++++++-- README.md | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78792c983..e468d4073 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,8 @@ option(SYSTEM_RTMIDI "Use a system-installed version of RtMidi instead of the ve option(SYSTEM_ZLIB "Use a system-installed version of zlib instead of the vendored one" OFF) option(SYSTEM_SDL2 "Use a system-installed version of SDL2 instead of the vendored one" ${SYSTEM_SDL2_DEFAULT}) option(WARNINGS_ARE_ERRORS "Whether warnings in furnace's C++ code should be treated as errors" OFF) +option(WITH_DEMOS "Install demo songs" ON) +option(WITH_INSTRUMENTS "Install instruments" ON) set(DEPENDENCIES_INCLUDE_DIRS "") @@ -709,8 +711,12 @@ if (NOT ANDROID OR TERMUX) install(FILES res/furnace.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) install(DIRECTORY papers DESTINATION ${CMAKE_INSTALL_DOCDIR}) install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/furnace) - install(DIRECTORY demos DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace) - install(DIRECTORY instruments DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace) + if (WITH_DEMOS) + install(DIRECTORY demos DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace) + endif() + if (WITH_INSTRUMENTS) + install(DIRECTORY instruments DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace) + endif() foreach(num 16 32 64 128 256 512) set(res ${num}x${num}) install(FILES res/icon.iconset/icon_${res}.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${res}/apps) diff --git a/README.md b/README.md index 1a2d88a7d..d2308dd75 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,8 @@ Available options: | `SYSTEM_ZLIB` | `OFF` | Use a system-installed version of zlib instead of the vendored one | | `SYSTEM_SDL2` | `OFF` | Use a system-installed version of SDL2 instead of the vendored one | | `WARNINGS_ARE_ERRORS` | `OFF` (but consider enabling this & reporting any errors that arise from it!) | Whether warnings in furnace's C++ code should be treated as errors | +| `WITH_DEMOS` | `ON` | Install demo songs on `make install` | +| `WITH_INSTRUMENTS` | `ON` | Install demo instruments on `make install` | ## console usage From 3a18e1e6fc4ef101b65a9d3ff5bc874fad430016 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Aug 2022 18:50:52 -0500 Subject: [PATCH 29/38] partially implement command stream binary mode --- papers/export-tech.md | 28 ++---- src/engine/engine.cpp | 192 ++++++++++++++++++++++++++++++++++++------ src/main.cpp | 9 +- 3 files changed, 181 insertions(+), 48 deletions(-) diff --git a/papers/export-tech.md b/papers/export-tech.md index 560aab324..dbfadece3 100644 --- a/papers/export-tech.md +++ b/papers/export-tech.md @@ -11,28 +11,16 @@ if it is a 2-byte macro, read a dummy byte. then read data. -## pattern data +## binary command stream -read sequentially. +read channel, command and values. -first byte determines what to read next: +if channel is 80 or higher, then it is a special command: ``` -NVI..EEE - -N: note -V: volume -I: instrument - -EEE: effect count (0-7) +fb xx xx xx xx: set tick rate +fc xx xx: wait xxxx ticks +fd xx: wait xx ticks +fe: wait one tick +ff: stop ``` - -if you read 0, end of pattern. -otherwise read in following order: - -1. note -2. volume -3. instrument -4. effect and effect value - -then read number of rows until next value, minus 1. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 5d9224002..fd94e521a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -235,8 +235,131 @@ double DivEngine::benchmarkSeek() { return tAvg; } +#define WRITE_TICK \ + if (!wroteTick) { \ + wroteTick=true; \ + if (binary) { \ + if (tick-lastTick>255) { \ + w->writeC(0xfc); \ + w->writeS(tick-lastTick); \ + } else if (tick-lastTick>1) { \ + w->writeC(0xfd); \ + w->writeC(tick-lastTick); \ + } else { \ + w->writeC(0xfe); \ + } \ + } else { \ + w->writeText(fmt::sprintf(">> TICK %d\n",tick)); \ + } \ + lastTick=tick; \ + } + +void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { + w->writeC(c.cmd); + switch (c.cmd) { + case DIV_CMD_NOTE_ON: + case DIV_CMD_HINT_LEGATO: + if (c.value==DIV_NOTE_NULL) { + w->writeC(0xff); + } else { + w->writeC(c.value+60); + } + break; + case DIV_CMD_NOTE_OFF: + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + break; + case DIV_CMD_INSTRUMENT: + case DIV_CMD_HINT_VIBRATO_RANGE: + case DIV_CMD_HINT_VIBRATO_SHAPE: + case DIV_CMD_HINT_PITCH: + case DIV_CMD_HINT_VOLUME: + case DIV_CMD_SAMPLE_MODE: + case DIV_CMD_SAMPLE_FREQ: + case DIV_CMD_SAMPLE_BANK: + case DIV_CMD_SAMPLE_POS: + case DIV_CMD_SAMPLE_DIR: + case DIV_CMD_FM_HARD_RESET: + case DIV_CMD_FM_LFO: + case DIV_CMD_FM_LFO_WAVE: + case DIV_CMD_FM_FB: + case DIV_CMD_FM_EXTCH: + case DIV_CMD_FM_AM_DEPTH: + case DIV_CMD_FM_PM_DEPTH: + case DIV_CMD_STD_NOISE_FREQ: + case DIV_CMD_STD_NOISE_MODE: + case DIV_CMD_WAVE: + case DIV_CMD_GB_SWEEP_TIME: + case DIV_CMD_GB_SWEEP_DIR: + case DIV_CMD_PCE_LFO_MODE: + case DIV_CMD_PCE_LFO_SPEED: + case DIV_CMD_NES_DMC: + case DIV_CMD_C64_CUTOFF: + case DIV_CMD_C64_RESONANCE: + case DIV_CMD_C64_FILTER_MODE: + case DIV_CMD_C64_RESET_TIME: + case DIV_CMD_C64_RESET_MASK: + case DIV_CMD_C64_FILTER_RESET: + case DIV_CMD_C64_DUTY_RESET: + case DIV_CMD_C64_EXTENDED: + case DIV_CMD_AY_ENVELOPE_SET: + case DIV_CMD_AY_ENVELOPE_LOW: + case DIV_CMD_AY_ENVELOPE_HIGH: + case DIV_CMD_AY_ENVELOPE_SLIDE: + case DIV_CMD_AY_NOISE_MASK_AND: + case DIV_CMD_AY_NOISE_MASK_OR: + case DIV_CMD_AY_AUTO_ENVELOPE: + w->writeC(c.value); + break; + case DIV_CMD_PANNING: + case DIV_CMD_HINT_VIBRATO: + case DIV_CMD_HINT_ARPEGGIO: + case DIV_CMD_HINT_PORTA: + case DIV_CMD_FM_TL: + case DIV_CMD_FM_AM: + case DIV_CMD_FM_AR: + case DIV_CMD_FM_DR: + case DIV_CMD_FM_SL: + case DIV_CMD_FM_D2R: + case DIV_CMD_FM_RR: + case DIV_CMD_FM_DT: + case DIV_CMD_FM_DT2: + case DIV_CMD_FM_RS: + case DIV_CMD_FM_KSR: + case DIV_CMD_FM_VIB: + case DIV_CMD_FM_SUS: + case DIV_CMD_FM_WS: + case DIV_CMD_FM_SSG: + case DIV_CMD_FM_REV: + case DIV_CMD_FM_EG_SHIFT: + case DIV_CMD_FM_MULT: + case DIV_CMD_FM_FINE: + case DIV_CMD_AY_IO_WRITE: + case DIV_CMD_AY_AUTO_PWM: + w->writeC(c.value); + w->writeC(c.value2); + break; + case DIV_CMD_PRE_PORTA: + w->writeC((c.value?0x80:0)|(c.value2?0x40:0)); + break; + case DIV_CMD_HINT_VOL_SLIDE: + case DIV_CMD_C64_FINE_DUTY: + case DIV_CMD_C64_FINE_CUTOFF: + w->writeS(c.value); + break; + case DIV_CMD_FM_FIXFREQ: + w->writeS((c.value<<12)|(c.value2&0x7ff)); + break; + case DIV_CMD_NES_SWEEP: + w->writeC((c.value?8:0)|(c.value2&0x77)); + break; + default: + logW("unimplemented command %s!",cmdName[c.cmd]); + break; + } +} + SafeWriter* DivEngine::saveCommand(bool binary) { - logI("implement! %d",binary); stop(); repeatPattern=false; setOrder(0); @@ -252,36 +375,43 @@ SafeWriter* DivEngine::saveCommand(bool binary) { w->init(); // write header - w->writeText("# Furnace Command Stream\n\n"); + if (binary) { + w->write("FCS",4); + } else { + w->writeText("# Furnace Command Stream\n\n"); - w->writeText("[Information]\n"); - w->writeText(fmt::sprintf("name: %s\n",song.name)); - w->writeText(fmt::sprintf("author: %s\n",song.author)); - w->writeText(fmt::sprintf("category: %s\n",song.category)); - w->writeText(fmt::sprintf("system: %s\n",song.systemName)); + w->writeText("[Information]\n"); + w->writeText(fmt::sprintf("name: %s\n",song.name)); + w->writeText(fmt::sprintf("author: %s\n",song.author)); + w->writeText(fmt::sprintf("category: %s\n",song.category)); + w->writeText(fmt::sprintf("system: %s\n",song.systemName)); - w->writeText("\n"); + w->writeText("\n"); - w->writeText("[SubSongInformation]\n"); - w->writeText(fmt::sprintf("name: %s\n",curSubSong->name)); - w->writeText(fmt::sprintf("tickRate: %f\n",curSubSong->hz)); + w->writeText("[SubSongInformation]\n"); + w->writeText(fmt::sprintf("name: %s\n",curSubSong->name)); + w->writeText(fmt::sprintf("tickRate: %f\n",curSubSong->hz)); - w->writeText("\n"); + w->writeText("\n"); - w->writeText("[SysDefinition]\n"); - // TODO + w->writeText("[SysDefinition]\n"); + // TODO - w->writeText("\n"); + w->writeText("\n"); + } // play the song ourselves bool done=false; playSub(false); - w->writeText("[Stream]\n"); + if (!binary) { + w->writeText("[Stream]\n"); + } int tick=0; bool oldCmdStreamEnabled=cmdStreamEnabled; cmdStreamEnabled=true; double curDivider=divider; + int lastTick=0; while (!done) { if (nextTick(false,true) || !playing) { done=true; @@ -290,11 +420,13 @@ SafeWriter* DivEngine::saveCommand(bool binary) { bool wroteTick=false; if (curDivider!=divider) { curDivider=divider; - if (!wroteTick) { - wroteTick=true; - w->writeText(fmt::sprintf(">> TICK %d\n",tick)); + WRITE_TICK; + if (binary) { + w->writeC(0xfb); + w->writeI((int)(curDivider*65536)); + } else { + w->writeText(fmt::sprintf(">> SET_RATE %f\n",curDivider)); } - w->writeText(fmt::sprintf(">> SET_RATE %f\n",curDivider)); } for (DivCommand& i: cmdStream) { switch (i.cmd) { @@ -314,11 +446,13 @@ SafeWriter* DivEngine::saveCommand(bool binary) { case DIV_CMD_PRE_NOTE: break; default: - if (!wroteTick) { - wroteTick=true; - w->writeText(fmt::sprintf(">> TICK %d\n",tick)); + WRITE_TICK; + if (binary) { + w->writeC(i.chan); + writePackedCommandValues(w,i); + } else { + w->writeText(fmt::sprintf(" %d: %s %d %d\n",i.chan,cmdName[i.cmd],i.value,i.value2)); } - w->writeText(fmt::sprintf(" %d: %s %d %d\n",i.chan,cmdName[i.cmd],i.value,i.value2)); break; } } @@ -327,10 +461,14 @@ SafeWriter* DivEngine::saveCommand(bool binary) { } cmdStreamEnabled=oldCmdStreamEnabled; - if (!playing) { - w->writeText(">> END\n"); + if (binary) { + w->writeC(0xff); } else { - w->writeText(">> LOOP 0\n"); + if (!playing) { + w->writeText(">> END\n"); + } else { + w->writeText(">> LOOP 0\n"); + } } remainingLoops=-1; diff --git a/src/main.cpp b/src/main.cpp index 6e0173aba..39c9e6ca2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,6 +64,7 @@ bool consoleMode=true; #endif bool displayEngineFailError=false; +bool cmdOutBinary=false; std::vector params; @@ -115,6 +116,11 @@ TAParamResult pConsole(String val) { return TA_PARAM_SUCCESS; } +TAParamResult pBinary(String val) { + cmdOutBinary=true; + return TA_PARAM_SUCCESS; +} + TAParamResult pLogLevel(String val) { if (val=="trace") { logLevel=LOGLEVEL_TRACE; @@ -273,6 +279,7 @@ void initParams() { params.push_back(TAParam("o","output",true,pOutput,"","output audio to file")); params.push_back(TAParam("O","vgmout",true,pVGMOut,"","output .vgm data")); params.push_back(TAParam("C","cmdout",true,pCmdOut,"","output command stream")); + params.push_back(TAParam("b","binary",false,pBinary,"","set command stream output format to binary")); params.push_back(TAParam("L","loglevel",true,pLogLevel,"debug|info|warning|error","set the log level (info by default)")); params.push_back(TAParam("v","view",true,pView,"pattern|commands|nothing","set visualization (pattern by default)")); params.push_back(TAParam("c","console",false,pConsole,"","enable console mode")); @@ -454,7 +461,7 @@ int main(int argc, char** argv) { } if (outName!="" || vgmOutName!="" || cmdOutName!="") { if (cmdOutName!="") { - SafeWriter* w=e.saveCommand(false); + SafeWriter* w=e.saveCommand(cmdOutBinary); if (w!=NULL) { FILE* f=fopen(cmdOutName.c_str(),"wb"); if (f!=NULL) { From 049ab065449d4237734ff6fb2214fe09482d255f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 4 Aug 2022 23:37:28 -0500 Subject: [PATCH 30/38] PCE: add option to pick A/non-A revision of chip --- src/engine/platform/pce.cpp | 13 +++++++++++-- src/gui/sysConf.cpp | 7 +++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index a61b61836..d46e012ce 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -569,6 +569,12 @@ void DivPlatformPCE::setFlags(unsigned int flags) { for (int i=0; i<6; i++) { oscBuf[i]->rate=rate; } + + if (pce!=NULL) { + delete pce; + pce=NULL; + } + pce=new PCE_PSG(tempL,tempR,(flags&4)?PCE_PSG::REVISION_HUC6280A:PCE_PSG::REVISION_HUC6280); } void DivPlatformPCE::poke(unsigned int addr, unsigned short val) { @@ -587,8 +593,8 @@ int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, unsigned int f isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; } + pce=NULL; setFlags(flags); - pce=new PCE_PSG(tempL,tempR,PCE_PSG::REVISION_HUC6280A); reset(); return 6; } @@ -597,7 +603,10 @@ void DivPlatformPCE::quit() { for (int i=0; i<6; i++) { delete oscBuf[i]; } - delete pce; + if (pce!=NULL) { + delete pce; + pce=NULL; + } } DivPlatformPCE::~DivPlatformPCE() { diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 7c624f309..4cc1784a6 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -118,6 +118,13 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool if (ImGui::Checkbox("Disable anti-click",&antiClick)) { copyOfFlags=(flags&(~8))|(antiClick<<3); } + ImGui::Text("Chip revision:"); + if (ImGui::RadioButton("HuC6280 (original)",(flags&4)==0)) { + copyOfFlags=(flags&(~4))|0; + } + if (ImGui::RadioButton("HuC6280A (SuperGrafx)",(flags&4)==4)) { + copyOfFlags=(flags&(~4))|4; + } break; } case DIV_SYSTEM_SOUND_UNIT: { From 827904d46e1999d421fd0b9917ce0266bef07fcf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 5 Aug 2022 00:05:36 -0500 Subject: [PATCH 31/38] what happens if I force arm64 on macOS CI? --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e060924f..ed4710879 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -201,7 +201,7 @@ jobs: elif [ '${{ matrix.config.compiler }}' == 'mingw' ]; then CMAKE_EXTRA_ARGS+=('-DCMAKE_TOOLCHAIN_FILE=scripts/Cross-MinGW-${{ steps.windows-identify.outputs.mingw-target }}.cmake') elif [ '${{ runner.os }}' == 'macOS' ]; then - CMAKE_EXTRA_ARGS+=('-DCMAKE_OSX_DEPLOYMENT_TARGET="10.9"') + CMAKE_EXTRA_ARGS+=('-DCMAKE_OSX_DEPLOYMENT_TARGET="10.9"' '-DCMAKE_OSX_ARCHITECTURES=arm64') fi cmake \ From a0968aed0719a032c131b24ce583abaaa2e25e00 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 5 Aug 2022 03:27:35 -0500 Subject: [PATCH 32/38] GUI: fix text/binary command stream outs being swa --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 28edd9e32..c1cd7f076 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3544,7 +3544,7 @@ bool FurnaceGUI::loop() { if (i>='A' && i<='Z') i+='a'-'A'; } bool isBinary=true; - if ((lowerCase.size()<4 || lowerCase.rfind(".txt")!=lowerCase.size()-4)) { + if ((lowerCase.size()<4 || lowerCase.rfind(".bin")!=lowerCase.size()-4)) { isBinary=false; } From 1d30febff8497b2bfdc99c81591914de3480b2af Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 5 Aug 2022 15:39:13 -0500 Subject: [PATCH 33/38] oh yeah --- .github/workflows/build.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ed4710879..5ee82f0da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,8 @@ jobs: - { name: 'Windows MSVC x86_64', os: windows-latest, compiler: msvc, arch: x86_64 } - { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 } - { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 } - - { name: 'macOS', os: macos-latest } + - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } + - { name: 'macOS ARM', os: macos-latest, arch: arm64 } - { name: 'Ubuntu', os: ubuntu-18.04 } fail-fast: false @@ -201,7 +202,11 @@ jobs: elif [ '${{ matrix.config.compiler }}' == 'mingw' ]; then CMAKE_EXTRA_ARGS+=('-DCMAKE_TOOLCHAIN_FILE=scripts/Cross-MinGW-${{ steps.windows-identify.outputs.mingw-target }}.cmake') elif [ '${{ runner.os }}' == 'macOS' ]; then - CMAKE_EXTRA_ARGS+=('-DCMAKE_OSX_DEPLOYMENT_TARGET="10.9"' '-DCMAKE_OSX_ARCHITECTURES=arm64') + if [ '${{ matrix.config.arch }}' == 'arm64' ]; then + CMAKE_EXTRA_ARGS+=('-DCMAKE_OSX_DEPLOYMENT_TARGET="11.0"' '-DCMAKE_OSX_ARCHITECTURES=arm64') + else + CMAKE_EXTRA_ARGS+=('-DCMAKE_OSX_DEPLOYMENT_TARGET="10.9"') + fi fi cmake \ From 3e70ed6d3eb22d42c51e030f367c5321796124bd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 5 Aug 2022 17:22:07 -0500 Subject: [PATCH 34/38] CI: fix artifact conflict --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ee82f0da..da0cfbfae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -77,7 +77,7 @@ jobs: package_name="${package_name}-${{ matrix.config.arch }}" package_ext="" # Directory, uploading will automatically zip it elif [ '${{ runner.os }}' == 'macOS' ]; then - package_name="${package_name}-macOS" + package_name="${package_name}-macOS-${{ matrix.config.arch }}" package_ext=".dmg" else package_name="${package_name}-Linux" From 6ec9cceb091dbd70966f099510f72d4be0db662e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 6 Aug 2022 00:34:28 -0500 Subject: [PATCH 35/38] PCE: remove some sample playback clicking --- src/engine/platform/pce.cpp | 9 ++++++++- src/engine/platform/pce.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index d46e012ce..757cff159 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -133,6 +133,10 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len) } void DivPlatformPCE::updateWave(int ch) { + if (chan[ch].pcm) { + chan[ch].deferredWaveUpdate=true; + return; + } chWrite(ch,0x04,0x5f); chWrite(ch,0x04,0x1f); for (int i=0; i<32; i++) { @@ -142,6 +146,9 @@ void DivPlatformPCE::updateWave(int ch) { if (chan[ch].active) { chWrite(ch,0x04,0x80|chan[ch].outVol); } + if (chan[ch].deferredWaveUpdate) { + chan[ch].deferredWaveUpdate=false; + } } // TODO: in octave 6 the noise table changes to a tonal one @@ -227,7 +234,7 @@ void DivPlatformPCE::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].active) { - if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) { + if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) || chan[i].deferredWaveUpdate) { updateWave(i); } } diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 22a24ddf8..17e191d44 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -33,7 +33,7 @@ class DivPlatformPCE: public DivDispatch { unsigned int dacPos; int dacSample, ins; unsigned char pan; - bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac, deferredWaveUpdate; signed char vol, outVol, wave; DivMacroInt std; DivWaveSynth ws; @@ -64,6 +64,7 @@ class DivPlatformPCE: public DivDispatch { noise(false), pcm(false), furnaceDac(false), + deferredWaveUpdate(false), vol(31), outVol(31), wave(-1) {} From 8a7d352ec6071c3d7f150953ba81f23c3b249f1d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 6 Aug 2022 00:38:24 -0500 Subject: [PATCH 36/38] PCE: fix phase reset macro when anti-click is on --- src/engine/platform/pce.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 757cff159..5d9a0de88 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -233,6 +233,10 @@ void DivPlatformPCE::tick(bool sysTick) { } chan[i].freqChanged=true; } + if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { + chan[i].antiClickWavePos=0; + chan[i].antiClickPeriodCount=0; + } if (chan[i].active) { if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) || chan[i].deferredWaveUpdate) { updateWave(i); From 0946d2388301cd59407d448a1e16686fe76a72e9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 6 Aug 2022 00:39:09 -0500 Subject: [PATCH 37/38] Game Boy: fix phase reset macro when anti-click is --- src/engine/platform/gb.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 4d10d7d22..4067e9f17 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -220,6 +220,10 @@ void DivPlatformGB::tick(bool sysTick) { if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; + if (i==2) { + antiClickWavePos=0; + antiClickPeriodCount=0; + } } } if (i==2) { From 5534f55f7aabb7814c65693972558a5062de61cc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 6 Aug 2022 04:04:18 -0500 Subject: [PATCH 38/38] dev104 - add "is sample" flag to Sound Unit ins --- papers/format.md | 3 +++ src/engine/engine.h | 4 ++-- src/engine/instrument.cpp | 10 ++++++++++ src/engine/instrument.h | 9 +++++++++ src/engine/platform/su.cpp | 6 +++--- src/gui/insEdit.cpp | 24 +++++++++++++++--------- 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/papers/format.md b/papers/format.md index ca8092a5a..97e22a4e5 100644 --- a/papers/format.md +++ b/papers/format.md @@ -810,6 +810,9 @@ size | description 1 | vib depth 1 | am depth 23 | reserved + --- | **Sound Unit data** (>=104) + 1 | use sample + 1 | switch roles of phase reset timer and frequency ``` # wavetable diff --git a/src/engine/engine.h b/src/engine/engine.h index 6f969efc9..256b0c96d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -45,8 +45,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev103" -#define DIV_ENGINE_VERSION 103 +#define DIV_VERSION "dev104" +#define DIV_ENGINE_VERSION 104 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index a76588561..4d274e18c 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -528,6 +528,10 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(0); } + // Sound Unit + w->writeC(su.useSample); + w->writeC(su.switchRoles); + blockEndSeek=w->tell(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); @@ -1075,6 +1079,12 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { for (int k=0; k<23; k++) reader.readC(); } + // Sound Unit + if (version>=104) { + su.useSample=reader.readC(); + su.switchRoles=reader.readC(); + } + return DIV_DATA_SUCCESS; } diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 18bf2c5f7..1c25988e4 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -437,6 +437,14 @@ struct DivInstrumentWaveSynth { param4(0) {} }; +struct DivInstrumentSoundUnit { + bool useSample; + bool switchRoles; + DivInstrumentSoundUnit(): + useSample(false), + switchRoles(false) {} +}; + struct DivInstrument { String name; bool mode; @@ -450,6 +458,7 @@ struct DivInstrument { DivInstrumentFDS fds; DivInstrumentMultiPCM multipcm; DivInstrumentWaveSynth ws; + DivInstrumentSoundUnit su; /** * save the instrument to a SafeWriter. diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 2d1a38932..4320798ab 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -256,12 +256,12 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SU); - if (chan[c.chan].pcm && ins->type!=DIV_INS_AMIGA) { - chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA); + if (chan[c.chan].pcm && !(ins->type==DIV_INS_AMIGA || ins->su.useSample)) { + chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->su.useSample); writeControl(c.chan); writeControlUpper(c.chan); } - chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA); + chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->su.useSample); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index f991b9e2a..8e405251e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -3093,13 +3093,17 @@ void FurnaceGUI::drawInsEdit() { P(ImGui::Checkbox("Don't test/gate before new note",&ins->c64.noTest)); ImGui::EndTabItem(); } - if (ins->type==DIV_INS_AMIGA) if (ImGui::BeginTabItem("Sample")) { + if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SU) if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) { String sName; if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) { sName="none selected"; } else { sName=e->song.sample[ins->amiga.initSample]->name; } + if (ins->type==DIV_INS_SU) { + P(ImGui::Checkbox("Use sample",&ins->su.useSample)); + P(ImGui::Checkbox("Switch roles of frequency and phase reset timer",&ins->su.switchRoles)); + } if (ImGui::BeginCombo("Initial Sample",sName.c_str())) { String id; for (int i=0; isong.sampleLen; i++) { @@ -3110,14 +3114,16 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndCombo(); } - P(ImGui::Checkbox("Use wavetable (Amiga only)",&ins->amiga.useWave)); - if (ins->amiga.useWave) { - int len=ins->amiga.waveLen+1; - if (ImGui::InputInt("Width",&len,2,16)) { - if (len<2) len=2; - if (len>256) len=256; - ins->amiga.waveLen=(len&(~1))-1; - PARAMETER + if (ins->type==DIV_INS_AMIGA) { + P(ImGui::Checkbox("Use wavetable (Amiga only)",&ins->amiga.useWave)); + if (ins->amiga.useWave) { + int len=ins->amiga.waveLen+1; + if (ImGui::InputInt("Width",&len,2,16)) { + if (len<2) len=2; + if (len>256) len=256; + ins->amiga.waveLen=(len&(~1))-1; + PARAMETER + } } } ImGui::BeginDisabled(ins->amiga.useWave);