From 69f95722f66a280b1de942b010c438593d890e1a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 7 Aug 2024 18:22:36 -0500 Subject: [PATCH 01/16] prepare to diagnose TIunA hang --- src/engine/tiunaOps.cpp | 110 ++++++++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/src/engine/tiunaOps.cpp b/src/engine/tiunaOps.cpp index 942822cdb..8d13ea5ae 100644 --- a/src/engine/tiunaOps.cpp +++ b/src/engine/tiunaOps.cpp @@ -26,53 +26,107 @@ #include "../ta-log.h" struct TiunaNew { - short pitch=-1; - signed char ins=-1; - signed char vol=-1; - short sync=-1; + short pitch; + signed char ins; + signed char vol; + short sync; + TiunaNew(): + pitch(-1), + ins(-1), + vol(-1), + sync(-1) {} }; + struct TiunaLast { - short pitch=0; - signed char ins=0; - signed char vol=0; - int tick=1; - bool forcePitch=true; + short pitch; + signed char ins; + signed char vol; + int tick; + bool forcePitch; + TiunaLast(): + pitch(0), + ins(0), + vol(0), + tick(1), + forcePitch(true) {} }; + struct TiunaCmd { - signed char pitchChange=-1; - short pitchSet=-1; - signed char ins=-1; - signed char vol=-1; - short sync=-1; - short wait=-1; + signed char pitchChange; + short pitchSet; + signed char ins; + signed char vol; + short sync; + short wait; + TiunaCmd(): + pitchChange(-1), + pitchSet(-1), + ins(-1), + vol(-1), + sync(-1), + wait(-1) {} }; + struct TiunaBytes { - unsigned char ch=0; - int ticks=0; - unsigned char size=0; + unsigned char ch; + int ticks; + unsigned char size; unsigned char buf[16]; friend bool operator==(const TiunaBytes& l, const TiunaBytes& r) { if (l.size!=r.size) return false; if (l.ticks!=r.ticks) return false; return memcmp(l.buf,r.buf,l.size)==0; } + TiunaBytes(unsigned char c, int t, unsigned char s, std::initializer_list b): + ch(c), + ticks(t), + size(s) { + // because C++14 does not support data() on initializer_list + unsigned char p=0; + for (unsigned char i: b) { + buf[p++]=i; + } + } + TiunaBytes(): + ch(0), + ticks(0), + size(0) { + memset(buf,0,16); + } }; + struct TiunaMatch { int pos; int endPos; int size; int id; + TiunaMatch(int p, int ep, int s, int _i): + pos(p), + endPos(ep), + size(s), + id(_i) {} + TiunaMatch(): + pos(0), + endPos(0), + size(0), + id(0) {} }; + struct TiunaMatches { - int bytesSaved=INT32_MIN; - int length=0; - int ticks=0; + int bytesSaved; + int length; + int ticks; std::vector pos; + TiunaMatches(): + bytesSaved(INT32_MIN), + length(0), + ticks(0) {} }; static void writeCmd(std::vector& cmds, TiunaCmd& cmd, unsigned char ch, int& lastWait, int fromTick, int toTick) { while (fromTick0); if (lastWait!=val) { cmd.wait=val; lastWait=val; @@ -322,7 +376,7 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, size+=renderedCmds[i+k].size; k++; } - if (size>2) match.push_back({j,j+k,size,0}); + if (size>2) match.push_back(TiunaMatch(j,j+k,size,0)); if (k==0) k++; j+=k; } @@ -462,7 +516,7 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, if (callVisited[cmIter->id]) { unsigned char idLo=cmIter->id&0xff; unsigned char idHi=cmIter->id>>8; - cmd={cmd.ch,0,2,{idHi,idLo}}; + cmd=TiunaBytes(cmd.ch,0,2,{idHi,idLo}); i=cmIter->endPos-1; } else { writeCall=cmIter->id; @@ -508,11 +562,11 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, totalSize++; logI("total size: %d bytes (%d banks)",totalSize,curBank+1); - FILE* f=ps_fopen("confirmedMatches.txt","wb"); - if (f!=NULL) { - fwrite(dbg.getFinalBuf(),1,dbg.size(),f); - fclose(f); - } + //FILE* f=ps_fopen("confirmedMatches.txt","wb"); + //if (f!=NULL) { + // fwrite(dbg.getFinalBuf(),1,dbg.size(),f); + // fclose(f); + //} return w; } From f7db34970d00a0486fb15f89af9b79133653b995 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 7 Aug 2024 18:33:07 -0500 Subject: [PATCH 02/16] add option to disable reportError() --- src/main.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9c3dc6a9e..1186d7d61 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -109,6 +109,8 @@ bool safeModeWithAudio=false; bool infoMode=false; +bool noReportError=false; + std::vector params; #ifdef HAVE_LOCALE @@ -199,6 +201,11 @@ TAParamResult pConsole(String val) { return TA_PARAM_SUCCESS; } +TAParamResult pQuiet(String val) { + noReportError=true; + return TA_PARAM_SUCCESS; +} + TAParamResult pNoStatus(String val) { consoleNoStatus=true; return TA_PARAM_SUCCESS; @@ -456,6 +463,7 @@ void initParams() { params.push_back(TAParam("v","view",true,pView,"pattern|commands|nothing","set visualization (nothing by default)")); params.push_back(TAParam("i","info",false,pInfo,"","get info about a song")); params.push_back(TAParam("c","console",false,pConsole,"","enable console mode")); + params.push_back(TAParam("q","noreport",false,pQuiet,"","do not display message box on error")); params.push_back(TAParam("n","nostatus",false,pNoStatus,"","disable playback status in console mode")); params.push_back(TAParam("N","nocontrols",false,pNoControls,"","disable standard input controls in console mode")); @@ -474,18 +482,25 @@ void initParams() { #ifdef _WIN32 void reportError(String what) { logE("%s",what); - MessageBox(NULL,what.c_str(),"Furnace",MB_OK|MB_ICONERROR); + if (!noReportError) { + MessageBox(NULL,what.c_str(),"Furnace",MB_OK|MB_ICONERROR); + } } #elif defined(ANDROID) || defined(__APPLE__) void reportError(String what) { logE("%s",what); #ifdef HAVE_SDL2 - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error",what.c_str(),NULL); + if (!noReportError) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,"Error",what.c_str(),NULL); + } #endif } #else void reportError(String what) { logE("%s",what); + if (!noReportError) { + // dummy + } } #endif From fb2b98a54165b4d2c49d1522fe3697d28aed5599 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 8 Aug 2024 00:51:46 -0500 Subject: [PATCH 03/16] prepare to add TIunA out parameter --- src/main.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1186d7d61..9bf68e722 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -87,6 +87,7 @@ String outName; String vgmOutName; String zsmOutName; String cmdOutName; +String tiunaOutName; int benchMode=0; int subsong=-1; DivAudioExportOptions exportOptions; @@ -441,6 +442,12 @@ TAParamResult pCmdOut(String val) { return TA_PARAM_SUCCESS; } +TAParamResult pTiunaOut(String val) { + tiunaOutName=val; + e.setAudio(DIV_AUDIO_DUMMY); + return TA_PARAM_SUCCESS; +} + bool needsValue(String param) { for (size_t i=0; i","output .zsm data for Commander X16 Zsound")); params.push_back(TAParam("C","cmdout",true,pCmdOut,"","output command stream")); + params.push_back(TAParam("T","tiunaout",true,pTiunaOut,"","output .asm data with TIunA sound data (TIA only)")); 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 (nothing by default)")); params.push_back(TAParam("i","info",false,pInfo,"","get info about a song")); @@ -562,6 +570,7 @@ int main(int argc, char** argv) { vgmOutName=""; zsmOutName=""; cmdOutName=""; + tiunaOutName=""; // load config for locale e.prePreInit(); @@ -729,14 +738,14 @@ int main(int argc, char** argv) { return 1; } - if (fileName.empty() && (benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) { + if (fileName.empty() && (benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="" || tiunaOutName!="")) { logE("provide a file!"); return 1; } #ifdef HAVE_GUI - if (e.preInit(consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) { - if (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="") { + if (e.preInit(consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="" || tiunaOutName!="")) { + if (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="" || tiunaOutName!="") { logW("engine wants safe mode, but Furnace GUI is not going to start."); } else { safeMode=true; @@ -748,7 +757,7 @@ int main(int argc, char** argv) { } #endif - if (safeMode && (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) { + if (safeMode && (consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="" || tiunaOutName!="")) { logE("you can't use safe mode and console/export mode together."); return 1; } @@ -757,7 +766,7 @@ int main(int argc, char** argv) { e.setAudio(DIV_AUDIO_DUMMY); } - if (!fileName.empty() && ((!e.getConfBool("tutIntroPlayed",TUT_INTRO_PLAYED)) || e.getConfInt("alwaysPlayIntro",0)!=3 || consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || cmdOutName!="")) { + if (!fileName.empty() && ((!e.getConfBool("tutIntroPlayed",TUT_INTRO_PLAYED)) || e.getConfInt("alwaysPlayIntro",0)!=3 || consoleMode || benchMode || infoMode || outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="" || tiunaOutName!="")) { logI("loading module..."); FILE* f=ps_fopen(fileName.c_str(),"rb"); if (f==NULL) { @@ -849,7 +858,7 @@ int main(int argc, char** argv) { return 0; } - if (outName!="" || vgmOutName!="" || cmdOutName!="") { + if (outName!="" || vgmOutName!="" || zsmOutName!="" || cmdOutName!="" || tiunaOutName!="") { if (cmdOutName!="") { SafeWriter* w=e.saveCommand(); if (w!=NULL) { From b213586ca413179242648f7e37c6863e604c86c9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 8 Aug 2024 15:30:17 -0500 Subject: [PATCH 04/16] add options for ZSM and TIunA out --- src/main.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 9bf68e722..70999859e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -112,6 +112,9 @@ bool infoMode=false; bool noReportError=false; +int tiunaFirstBankSize=3072; +int tiunaOtherBankSize=4096-48; + std::vector params; #ifdef HAVE_LOCALE @@ -891,6 +894,39 @@ int main(int argc, char** argv) { reportError(_("could not write VGM!")); } } + if (zsmOutName!="") { + // TODO: changing parameters + SafeWriter* w=e.saveZSM(60,true,true); + if (w!=NULL) { + FILE* f=ps_fopen(zsmOutName.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(fmt::sprintf(_("could not write ZSM! (%s)"),e.getLastError())); + } + } + if (tiunaOutName!="") { + SafeWriter* w=e.saveTiuna(NULL,"asmBaseLabel",tiunaFirstBankSize,tiunaOtherBankSize); + if (w!=NULL) { + FILE* f=ps_fopen(tiunaOutName.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(fmt::sprintf("could not write TIunA! (%s)",e.getLastError())); + } + } if (outName!="") { e.setConsoleMode(true); e.saveAudio(outName.c_str(),exportOptions); From d8aa07bbbb0d997f9f04608a626318bfb06a396e Mon Sep 17 00:00:00 2001 From: DevEd Date: Thu, 8 Aug 2024 17:36:55 -0400 Subject: [PATCH 05/16] GB: enable VIN input to accomodate multiple chips on hardware --- src/engine/platform/gb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 57860b97a..8c648612a 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -651,7 +651,7 @@ void DivPlatformGB::reset() { immWrite(0x26,0x8f); lastPan=0xff; immWrite(0x25,procMute()); - immWrite(0x24,0x77); + immWrite(0x24,0xff); antiClickPeriodCount=0; antiClickWavePos=0; From d44029647c116c59281d380461e49a519b0a0261 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 9 Aug 2024 00:30:16 -0500 Subject: [PATCH 06/16] TIunA export: possibly optimize --- src/engine/tiunaOps.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/engine/tiunaOps.cpp b/src/engine/tiunaOps.cpp index 8d13ea5ae..8a045cdf8 100644 --- a/src/engine/tiunaOps.cpp +++ b/src/engine/tiunaOps.cpp @@ -347,9 +347,13 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, std::vector callTicks; int cmId=0; int cmdSize=renderedCmds.size(); - std::vector processed=std::vector(cmdSize,false); + bool* processed=new bool[cmdSize]; + memset(processed,0,cmdSize*sizeof(bool)); + logI("max cmId: %d",(MAX(firstBankSize/1024,1))*256); while (firstBankSize>768 && cmId<(MAX(firstBankSize/1024,1))*256) { + logI("start CM %04x...",cmId); std::map potentialMatches; + logD("scan %d size...",cmdSize-1); for (int i=0; i match; int ch=renderedCmds[i].ch; for (int j=i+1; j=cmdSize) break; int k=0; int ticks=0; @@ -421,9 +426,13 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, } i++; } - if (potentialMatches.empty()) break; + if (potentialMatches.empty()) { + logV("potentialMatches is empty"); + break; + } int maxPMIdx=0; int maxPMVal=0; + logV("looking through potentialMatches..."); for (const auto& i: potentialMatches) { if (i.second.bytesSaved>maxPMVal) { maxPMVal=i.second.bytesSaved; @@ -431,14 +440,17 @@ SafeWriter* DivEngine::saveTiuna(const bool* sysToExport, const char* baseLabel, } } int maxPMLen=potentialMatches[maxPMIdx].length; + logV("the other step..."); for (const int i: potentialMatches[maxPMIdx].pos) { confirmedMatches.push_back({i,i+maxPMLen,0,cmId}); - std::fill(processed.begin()+i,processed.begin()+(i+maxPMLen),true); + memset(processed+i,1,maxPMLen); + //std::fill(processed.begin()+i,processed.begin()+(i+maxPMLen),true); } callTicks.push_back(potentialMatches[maxPMIdx].ticks); logI("CM %04x added: pos=%d,len=%d,matches=%d,saved=%d",cmId,maxPMIdx,maxPMLen,potentialMatches[maxPMIdx].pos.size(),maxPMVal); cmId++; } + delete[] processed; std::sort(confirmedMatches.begin(),confirmedMatches.end(),[](const TiunaMatch& l, const TiunaMatch& r){ return l.pos Date: Fri, 9 Aug 2024 04:27:18 -0500 Subject: [PATCH 07/16] DivROMExport: new functions for threading... --- src/engine/export.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/engine/export.h b/src/engine/export.h index d8e1878f1..bc9bf6855 100644 --- a/src/engine/export.h +++ b/src/engine/export.h @@ -45,9 +45,18 @@ struct DivROMExportOutput { data(NULL) {} }; +struct DivROMExportProgress { + String name; + float amount; +}; + class DivROMExport { public: - virtual std::vector go(DivEngine* e); + virtual bool go(DivEngine* eng); + virtual void abort(); + virtual std::vector getResult(); + virtual bool hasFailed(); + virtual DivROMExportProgress getProgress(); virtual ~DivROMExport() {} }; From a9130a1e883e8134edabf29bfff3b74faf49be94 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 9 Aug 2024 16:03:18 -0500 Subject: [PATCH 08/16] two demo songs by: - Crisps - Molkirill --- demos/a2600/TIADeepIntoCode.fur | Bin 0 -> 1427 bytes demos/genesis/imaginarium.fur | Bin 0 -> 8722 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/a2600/TIADeepIntoCode.fur create mode 100644 demos/genesis/imaginarium.fur diff --git a/demos/a2600/TIADeepIntoCode.fur b/demos/a2600/TIADeepIntoCode.fur new file mode 100644 index 0000000000000000000000000000000000000000..0a952a9c96b2256ad522441b42347e755c0c12d5 GIT binary patch literal 1427 zcmV;E1#J3woW)m5Y#T)w{%2>_Z^uaiDUvH?kvPDiRf^z%Xd!MMC{mL~eUX()h}&!% zCwA=GPSVg<0%<|w!U?EAf+F>b#DQEY@woTEu?G&TO58ah@eq>vW_EY7b`s){hD`fu zzxikWf4=!=_ILKs(0p|+f6=i@<*E6ibLbHfS+w4UM~6=Ka}XgIQRR3a9VF2iQDe^l ztMu=!Z=1G_JI(U#X6$VnaGZ)&n5&nq`m|&9m!}*$UcPW)$r`H`<}Ny~TK%KvEOh&; zg<7QshoAe&Fqm{@EC|BbH`vCQAo>;f+9W!K4E_n+juIV+5q%F#$BDWUL=ONbNz{=d zx(D>8iM|77JBa=Q?quN8N%SRfxQpl^&~*TP;P6v&y$DkcI+!q{9_NY^OG$2UVH!LV zjcFcFBpKuD$zFb)f<4IpBG`^0yYw;sc`aAZR}0pe;hgpSVxc~5)ykLZi}|W!RSNZs z)7DhAu;5f*prh8n$&(}2Yt7NodV>_H*hK280jAsrfcy}0L&yywFNC}haze-lAs3WU z-aj=MU;rEtfC)r^C=dhUKmtetDIg7W02zSDg})1b7yd5%UHH53cj52C--W*me;58P z{9X9F@OR-aeS=MiNOPlA?B_0U9~e53djm&BL5jv?hK?OcP;OxORNBvh+%%I%B8?(Q za)=L)o__HYJRcz~qq#H<&(-SH`I0kNw|Z%4GZTllU!*PNK|$8Jcm;gl48@ z5zV5S#Wag+me4GzSxU3CW*wSkT9^`hFSrcZA~PORauPoz!G^L$W20%$W_ew2Uh_~$ zC~j=Fq{i}cEKdsAlCjaWXS2MnH?Mg}3H?e5#jDm+VwrZLx0kH;lFrQrR{0h zWY&zDH6cwCq>!C4Hk#C|x1{Mjx|fyGOG>GuhQ{U4c=+&93H&izj@hJ;b&QRsJ)7lq zy?M<;O6Z45DBq7;%Fl`wehzpO7#GxS3@AzO`!6_UC+U}uc2dorAjER_J4z1p zL~RtKAj|uJ^;(S|98Zl`ux(wiZC$W!U9fFkux(wiZC$W!U9fFkux(wiZC$W!U9fFk zux(wiZC$1V7CK3kKwLTDZR2fDe9hm|M2Mf?!afO}=er2t*_9mi^~(4YkfTp!hyGcc zi$6kIP;`@LrIxc8zwFPG{8ETdg?PTjKFxO?pj&*f5;%o!xcR?pX|c1lkQ)3ok}R1B zcEu!K2Nr>A{!{j)y#mxiycn{{=aN4+@VgPRUGeADTVj-S9HmBm`7#vp4bH(rSApw( z@MYkp&jb6C&u9F=a)Gk%FXL3rV3T~_IY8#9UzWP*hWqDwsK^BFw_SpwtX1 hHA$c-6MVP(@!zy*k`Kn@$R=r~nCMRO#y>w9*rjq?%Ao)N literal 0 HcmV?d00001 diff --git a/demos/genesis/imaginarium.fur b/demos/genesis/imaginarium.fur new file mode 100644 index 0000000000000000000000000000000000000000..842ec8575b5feca7228e60eb5dfb529c5740e8bc GIT binary patch literal 8722 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 Date: Fri, 9 Aug 2024 16:04:54 -0500 Subject: [PATCH 09/16] one more demo song poltvick --- demos/a2600/Watch My Tone.fur | Bin 0 -> 2240 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/a2600/Watch My Tone.fur diff --git a/demos/a2600/Watch My Tone.fur b/demos/a2600/Watch My Tone.fur new file mode 100644 index 0000000000000000000000000000000000000000..8bfe4df17b1b95a264a3e041b098e264a76121c6 GIT binary patch literal 2240 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 literal 0 HcmV?d00001 From 4fe3822a7eee45b54c871690a6271497833df4b4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 9 Aug 2024 16:06:34 -0500 Subject: [PATCH 10/16] 32x32sam --- wavetables/32x32/32x32sam.fuw | Bin 0 -> 169 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 wavetables/32x32/32x32sam.fuw diff --git a/wavetables/32x32/32x32sam.fuw b/wavetables/32x32/32x32sam.fuw new file mode 100644 index 0000000000000000000000000000000000000000..4df39259cd81df568cdb33087dff088959b52a3d GIT binary patch literal 169 zcmdOOD=o@POiooOPb^CDIf-^ hfng9|6o`@4g4DzGfaE}GkTFaQM1wF$Eyzw7007tO3cmmV literal 0 HcmV?d00001 From 48523add008752cf68c1ec348e9aeba90eba1357 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 10 Aug 2024 02:16:30 -0500 Subject: [PATCH 11/16] asfgdhk;lj --- src/engine/export.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/export.h b/src/engine/export.h index bc9bf6855..bfd1049c0 100644 --- a/src/engine/export.h +++ b/src/engine/export.h @@ -51,6 +51,9 @@ struct DivROMExportProgress { }; class DivROMExport { + std::vector exportLog; + std::mutex logLock; + void logAppend(String what); public: virtual bool go(DivEngine* eng); virtual void abort(); From a4aa40891288a1778d8d02aa58efd0badbe7af98 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 10 Aug 2024 19:25:01 -0500 Subject: [PATCH 12/16] apvr eatavel eao rso jyrsovrs to-do: - make an "exporting ROM" dialog with progress and all - move TIunA export to the ROM export framework - perhaps do the same with ZSM in the future? --- src/engine/engine.h | 5 ++-- src/engine/export.cpp | 6 ++--- src/engine/export.h | 11 +++++--- src/engine/export/abstract.cpp | 31 +++++++++++++++++++++-- src/engine/export/amigaValidation.cpp | 23 +++++++++-------- src/engine/export/amigaValidation.h | 5 +++- src/gui/editControls.cpp | 35 -------------------------- src/gui/exportOptions.cpp | 36 +++++++++++++++++---------- 8 files changed, 80 insertions(+), 72 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 2a7718c1d..a2e900c81 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -697,9 +697,8 @@ class DivEngine { // save as .fur. // if notPrimary is true then the song will not be altered SafeWriter* saveFur(bool notPrimary=false, bool newPatternFormat=true); - // build a ROM file (TODO). - // specify system to build ROM for. - std::vector buildROM(DivROMExportOptions sys); + // return a ROM exporter. + DivROMExport* buildROM(DivROMExportOptions sys); // dump to VGM. // set trailingTicks to: // - 0 to add one tick of trailing diff --git a/src/engine/export.cpp b/src/engine/export.cpp index 2ea35327f..68794069e 100644 --- a/src/engine/export.cpp +++ b/src/engine/export.cpp @@ -21,7 +21,7 @@ #include "export/amigaValidation.h" -std::vector DivEngine::buildROM(DivROMExportOptions sys) { +DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) { DivROMExport* exporter=NULL; switch (sys) { case DIV_ROM_AMIGA_VALIDATION: @@ -31,7 +31,5 @@ std::vector DivEngine::buildROM(DivROMExportOptions sys) { exporter=new DivROMExport; break; } - std::vector ret=exporter->go(this); - delete exporter; - return ret; + return exporter; } diff --git a/src/engine/export.h b/src/engine/export.h index bfd1049c0..9f7ed8abd 100644 --- a/src/engine/export.h +++ b/src/engine/export.h @@ -51,13 +51,16 @@ struct DivROMExportProgress { }; class DivROMExport { - std::vector exportLog; - std::mutex logLock; - void logAppend(String what); + protected: + std::vector exportLog; + std::vector output; + std::mutex logLock; + void logAppend(String what); public: virtual bool go(DivEngine* eng); virtual void abort(); - virtual std::vector getResult(); + virtual void wait(); + std::vector& getResult(); virtual bool hasFailed(); virtual DivROMExportProgress getProgress(); virtual ~DivROMExport() {} diff --git a/src/engine/export/abstract.cpp b/src/engine/export/abstract.cpp index bd39e3aa0..a30fbecdd 100644 --- a/src/engine/export/abstract.cpp +++ b/src/engine/export/abstract.cpp @@ -20,7 +20,34 @@ #include "../export.h" #include "../../ta-log.h" -std::vector DivROMExport::go(DivEngine* e) { +bool DivROMExport::go(DivEngine* eng) { logW("what's this? the null ROM export?"); - return std::vector(); + return false; +} + +void DivROMExport::abort() { +} + +std::vector& DivROMExport::getResult() { + return output; +} + +bool DivROMExport::hasFailed() { + return true; +} + +DivROMExportProgress DivROMExport::getProgress() { + DivROMExportProgress ret; + ret.name="Test"; + ret.amount=0.0f; + return ret; +} + +void DivROMExport::logAppend(String what) { + logLock.lock(); + exportLog.push_back(what); + logLock.unlock(); +} + +void DivROMExport::wait() { } diff --git a/src/engine/export/amigaValidation.cpp b/src/engine/export/amigaValidation.cpp index a613ee422..3e2074bd0 100644 --- a/src/engine/export/amigaValidation.cpp +++ b/src/engine/export/amigaValidation.cpp @@ -40,8 +40,7 @@ struct SampleBookEntry { len(0) {} }; -std::vector DivExportAmigaValidation::go(DivEngine* e) { - std::vector ret; +void DivExportAmigaValidation::run() { std::vector waves; std::vector sampleBook; unsigned int wavesDataPtr=0; @@ -266,12 +265,16 @@ std::vector DivExportAmigaValidation::go(DivEngine* e) { } // finish - ret.reserve(5); - ret.push_back(DivROMExportOutput("sbook.bin",sbook)); - ret.push_back(DivROMExportOutput("wbook.bin",wbook)); - ret.push_back(DivROMExportOutput("sample.bin",sample)); - ret.push_back(DivROMExportOutput("wave.bin",wave)); - ret.push_back(DivROMExportOutput("seq.bin",seq)); - - return ret; + output.reserve(5); + output.push_back(DivROMExportOutput("sbook.bin",sbook)); + output.push_back(DivROMExportOutput("wbook.bin",wbook)); + output.push_back(DivROMExportOutput("sample.bin",sample)); + output.push_back(DivROMExportOutput("wave.bin",wave)); + output.push_back(DivROMExportOutput("seq.bin",seq)); +} + +bool DivExportAmigaValidation::go(DivEngine* eng) { + e=eng; + exportThread=new std::thread(&DivExportAmigaValidation::run,this); + return true; } diff --git a/src/engine/export/amigaValidation.h b/src/engine/export/amigaValidation.h index 11a4ecd48..46c693880 100644 --- a/src/engine/export/amigaValidation.h +++ b/src/engine/export/amigaValidation.h @@ -20,7 +20,10 @@ #include "../export.h" class DivExportAmigaValidation: public DivROMExport { + DivEngine* e; + std::thread* exportThread; + void run(); public: - std::vector go(DivEngine* e); + bool go(DivEngine* e); ~DivExportAmigaValidation() {} }; diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 5527f5598..3eba0298c 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -619,41 +619,6 @@ void FurnaceGUI::drawMobileControls() { if (ImGui::Button(_("Switch to Desktop Mode"))) { toggleMobileUI(!mobileUI); } - - int numAmiga=0; - for (int i=0; isong.systemLen; i++) { - if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++; - } - - if (numAmiga) { - ImGui::Text(_( - "this is NOT ROM export! only use for making sure the\n" - "Furnace Amiga emulator is working properly by\n" - "comparing it with real Amiga output." - )); - ImGui::AlignTextToFramePadding(); - ImGui::Text(_("Directory")); - ImGui::SameLine(); - ImGui::InputText("##AVDPath",&workingDirROMExport); - if (ImGui::Button(_("Bake Data"))) { - std::vector out=e->buildROM(DIV_ROM_AMIGA_VALIDATION); - if (workingDirROMExport.size()>0) { - if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR; - } - for (DivROMExportOutput& i: out) { - String path=workingDirROMExport+i.name; - FILE* outFile=ps_fopen(path.c_str(),"wb"); - if (outFile!=NULL) { - fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile); - fclose(outFile); - } - i.data->finish(); - delete i.data; - } - showError(fmt::sprintf(_("Done! Baked %d files."),(int)out.size())); - } - } - break; } } diff --git a/src/gui/exportOptions.cpp b/src/gui/exportOptions.cpp index 19077c13c..4b4360839 100644 --- a/src/gui/exportOptions.cpp +++ b/src/gui/exportOptions.cpp @@ -329,21 +329,31 @@ void FurnaceGUI::drawExportAmigaVal(bool onWindow) { ImGui::SameLine(); } if (ImGui::Button(_("Bake Data"),ImVec2(200.0f*dpiScale,0))) { - std::vector out=e->buildROM(DIV_ROM_AMIGA_VALIDATION); - if (workingDirROMExport.size()>0) { - if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR; - } - for (DivROMExportOutput& i: out) { - String path=workingDirROMExport+i.name; - FILE* outFile=ps_fopen(path.c_str(),"wb"); - if (outFile!=NULL) { - fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile); - fclose(outFile); + DivROMExport* ex=e->buildROM(DIV_ROM_AMIGA_VALIDATION); + if (ex->go(e)) { + ex->wait(); + if (ex->hasFailed()) { + showError("error!"); + } else { + if (workingDirROMExport.size()>0) { + if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR; + } + for (DivROMExportOutput& i: ex->getResult()) { + String path=workingDirROMExport+i.name; + FILE* outFile=ps_fopen(path.c_str(),"wb"); + if (outFile!=NULL) { + fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile); + fclose(outFile); + } + i.data->finish(); + delete i.data; + } + showError(fmt::sprintf(_("Done! Baked %d files."),(int)ex->getResult().size())); } - i.data->finish(); - delete i.data; + } else { + showError("error!"); } - showError(fmt::sprintf(_("Done! Baked %d files."),(int)out.size())); + delete ex; ImGui::CloseCurrentPopup(); } } From 148f49eb2ba088ae4c62585a09296e503554d7f9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 10 Aug 2024 19:38:50 -0500 Subject: [PATCH 13/16] further preparations --- src/engine/export.h | 1 + src/engine/export/abstract.cpp | 4 ++++ src/engine/export/amigaValidation.cpp | 14 ++++++++++++++ src/engine/export/amigaValidation.h | 3 +++ 4 files changed, 22 insertions(+) diff --git a/src/engine/export.h b/src/engine/export.h index 9f7ed8abd..9445ecfe9 100644 --- a/src/engine/export.h +++ b/src/engine/export.h @@ -62,6 +62,7 @@ class DivROMExport { virtual void wait(); std::vector& getResult(); virtual bool hasFailed(); + virtual bool isRunning(); virtual DivROMExportProgress getProgress(); virtual ~DivROMExport() {} }; diff --git a/src/engine/export/abstract.cpp b/src/engine/export/abstract.cpp index a30fbecdd..c33c38502 100644 --- a/src/engine/export/abstract.cpp +++ b/src/engine/export/abstract.cpp @@ -51,3 +51,7 @@ void DivROMExport::logAppend(String what) { void DivROMExport::wait() { } + +bool DivROMExport::isRunning() { + return false; +} diff --git a/src/engine/export/amigaValidation.cpp b/src/engine/export/amigaValidation.cpp index 3e2074bd0..2d9072aaa 100644 --- a/src/engine/export/amigaValidation.cpp +++ b/src/engine/export/amigaValidation.cpp @@ -271,10 +271,24 @@ void DivExportAmigaValidation::run() { output.push_back(DivROMExportOutput("sample.bin",sample)); output.push_back(DivROMExportOutput("wave.bin",wave)); output.push_back(DivROMExportOutput("seq.bin",seq)); + + running=false; } bool DivExportAmigaValidation::go(DivEngine* eng) { e=eng; + running=true; exportThread=new std::thread(&DivExportAmigaValidation::run,this); return true; } + +void DivExportAmigaValidation::wait() { + if (exportThread!=NULL) { + exportThread->join(); + delete exportThread; + } +} + +bool DivExportAmigaValidation::isRunning() { + return running; +} diff --git a/src/engine/export/amigaValidation.h b/src/engine/export/amigaValidation.h index 46c693880..b3e57382c 100644 --- a/src/engine/export/amigaValidation.h +++ b/src/engine/export/amigaValidation.h @@ -22,8 +22,11 @@ class DivExportAmigaValidation: public DivROMExport { DivEngine* e; std::thread* exportThread; + bool running; void run(); public: bool go(DivEngine* e); + void abort(); + void wait(); ~DivExportAmigaValidation() {} }; From 0325f9ed574b54e4de26e3bfaf666898de1df7ad Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 10 Aug 2024 19:50:58 -0500 Subject: [PATCH 14/16] NO --- src/engine/export/amigaValidation.cpp | 4 ++++ src/engine/export/amigaValidation.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/engine/export/amigaValidation.cpp b/src/engine/export/amigaValidation.cpp index 2d9072aaa..e9a8a8b84 100644 --- a/src/engine/export/amigaValidation.cpp +++ b/src/engine/export/amigaValidation.cpp @@ -289,6 +289,10 @@ void DivExportAmigaValidation::wait() { } } +void DivExportAmigaValidation::abort() { + wait(); +} + bool DivExportAmigaValidation::isRunning() { return running; } diff --git a/src/engine/export/amigaValidation.h b/src/engine/export/amigaValidation.h index b3e57382c..14c20fa05 100644 --- a/src/engine/export/amigaValidation.h +++ b/src/engine/export/amigaValidation.h @@ -26,6 +26,7 @@ class DivExportAmigaValidation: public DivROMExport { void run(); public: bool go(DivEngine* e); + bool isRunning(); void abort(); void wait(); ~DivExportAmigaValidation() {} From 8d005f7cbc2fa8d2316c46d82fe67dccd4941a73 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 10 Aug 2024 22:49:22 -0500 Subject: [PATCH 15/16] fix Linux build --- src/engine/export/amigaValidation.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/export/amigaValidation.h b/src/engine/export/amigaValidation.h index 14c20fa05..42703a36e 100644 --- a/src/engine/export/amigaValidation.h +++ b/src/engine/export/amigaValidation.h @@ -19,6 +19,8 @@ #include "../export.h" +#include + class DivExportAmigaValidation: public DivROMExport { DivEngine* e; std::thread* exportThread; From 035de0e767f2ffaf6473bbf378a0afea8f6d1072 Mon Sep 17 00:00:00 2001 From: Background <76162743+YaIiya@users.noreply.github.com> Date: Fri, 9 Aug 2024 23:10:09 +0700 Subject: [PATCH 16/16] opl the third --- .../I won't be there again (Extended Mix).fur | Bin 0 -> 29107 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/opl/I won't be there again (Extended Mix).fur diff --git a/demos/opl/I won't be there again (Extended Mix).fur b/demos/opl/I won't be there again (Extended Mix).fur new file mode 100644 index 0000000000000000000000000000000000000000..81e6621dbd8178690dc1d222cce5839e0d1af218 GIT binary patch literal 29107 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 O^Y218<*F;48~+bt0YwM^ literal 0 HcmV?d00001