diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eaf3dc1e..5ddba5cb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1138,15 +1138,7 @@ if (USE_BACKWARD) list(APPEND USED_SOURCES src/backtrace.cpp) if (WIN32) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag(-gcodeview GCC_CODEVIEW) - if (GCC_CODEVIEW OR FORCE_CODEVIEW) - set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ") - add_compile_options(-gcodeview) - message(STATUS "Enabling -gcodeview flag for backward-cpp.") - else() - message(WARNING "Could not enable -gcodeview! backward-cpp will not work.") - endif() + message(WARNING "Could not enable -gcodeview! backward-cpp will not work.") list(APPEND DEPENDENCIES_LIBRARIES dbghelp psapi) endif() endif() diff --git a/demos/a2600/the_erfngjt.fur b/demos/a2600/the_erfngjt.fur deleted file mode 100644 index 5d70dca8e..000000000 Binary files a/demos/a2600/the_erfngjt.fur and /dev/null differ diff --git a/demos/arcade/Crystal Dimension Section 2 WSG Mix.fur b/demos/arcade/Crystal Dimension Section 2 WSG Mix.fur new file mode 100644 index 000000000..d5511c682 Binary files /dev/null and b/demos/arcade/Crystal Dimension Section 2 WSG Mix.fur differ diff --git a/demos/arcade/JoyfulStart_Seta.fur b/demos/arcade/JoyfulStart_Seta.fur new file mode 100644 index 000000000..a6871cca0 Binary files /dev/null and b/demos/arcade/JoyfulStart_Seta.fur differ diff --git a/demos/arcade/REPROGRAMMED.fur b/demos/arcade/REPROGRAMMED.fur new file mode 100644 index 000000000..0764b4133 Binary files /dev/null and b/demos/arcade/REPROGRAMMED.fur differ diff --git a/demos/arcade/SHIN_YA.fur b/demos/arcade/SHIN_YA.fur new file mode 100644 index 000000000..423564e15 Binary files /dev/null and b/demos/arcade/SHIN_YA.fur differ diff --git a/demos/arcade/Slow Brew.fur b/demos/arcade/Slow Brew.fur new file mode 100644 index 000000000..f8038040e Binary files /dev/null and b/demos/arcade/Slow Brew.fur differ diff --git a/demos/arcade/living crystal cave.fur b/demos/arcade/living crystal cave.fur new file mode 100644 index 000000000..944c6ed17 Binary files /dev/null and b/demos/arcade/living crystal cave.fur differ diff --git a/demos/c64/The_Snippet.fur b/demos/c64/The_Snippet.fur deleted file mode 100644 index b4441d607..000000000 Binary files a/demos/c64/The_Snippet.fur and /dev/null differ diff --git a/demos/gameboy/dtect.fur b/demos/gameboy/dtect.fur deleted file mode 100644 index 17c5f5bc4..000000000 Binary files a/demos/gameboy/dtect.fur and /dev/null differ diff --git a/demos/gameboy/freedom.fur b/demos/gameboy/freedom.fur deleted file mode 100644 index 024bafded..000000000 Binary files a/demos/gameboy/freedom.fur and /dev/null differ diff --git a/demos/genesis/Carnage.fur b/demos/genesis/Carnage.fur deleted file mode 100644 index 8f4acfefd..000000000 Binary files a/demos/genesis/Carnage.fur and /dev/null differ diff --git a/demos/genesis/yky.fur b/demos/genesis/yky.fur deleted file mode 100644 index 0074ed545..000000000 Binary files a/demos/genesis/yky.fur and /dev/null differ diff --git a/demos/misc/Back_In_Detroit_Supervision.fur b/demos/misc/Back_In_Detroit_Supervision.fur new file mode 100644 index 000000000..54c228496 Binary files /dev/null and b/demos/misc/Back_In_Detroit_Supervision.fur differ diff --git a/demos/misc/OHM_VRC6.fur b/demos/misc/OHM_VRC6.fur deleted file mode 100644 index e28927225..000000000 Binary files a/demos/misc/OHM_VRC6.fur and /dev/null differ diff --git a/demos/misc/david.fur b/demos/misc/david.fur new file mode 100644 index 000000000..d633eeb78 Binary files /dev/null and b/demos/misc/david.fur differ diff --git a/demos/misc/empty_PV-1000.fur b/demos/misc/empty_PV-1000.fur deleted file mode 100644 index d6ec13ce5..000000000 Binary files a/demos/misc/empty_PV-1000.fur and /dev/null differ diff --git a/demos/misc/hittingtherevlimiter_RF5C68.fur b/demos/misc/hittingtherevlimiter_RF5C68.fur new file mode 100644 index 000000000..87bcb7929 Binary files /dev/null and b/demos/misc/hittingtherevlimiter_RF5C68.fur differ diff --git a/demos/misc/rf5wapianoroll.fur b/demos/misc/rf5wapianoroll.fur deleted file mode 100644 index 4b2919f3f..000000000 Binary files a/demos/misc/rf5wapianoroll.fur and /dev/null differ diff --git a/demos/misc/sawmen_break_SM8521.fur b/demos/misc/sawmen_break_SM8521.fur new file mode 100644 index 000000000..413ec3f00 Binary files /dev/null and b/demos/misc/sawmen_break_SM8521.fur differ diff --git a/demos/misc/spite_is_the_best_motivator_PV1000.fur b/demos/misc/spite_is_the_best_motivator_PV1000.fur new file mode 100644 index 000000000..c934dfdd3 Binary files /dev/null and b/demos/misc/spite_is_the_best_motivator_PV1000.fur differ diff --git a/demos/misc/tech_low_qual_era_RF5C68.fur b/demos/misc/tech_low_qual_era_RF5C68.fur new file mode 100644 index 000000000..487d2adfa Binary files /dev/null and b/demos/misc/tech_low_qual_era_RF5C68.fur differ diff --git a/demos/multichip/AgentX.fur b/demos/multichip/AgentX.fur deleted file mode 100644 index 07fe08320..000000000 Binary files a/demos/multichip/AgentX.fur and /dev/null differ diff --git a/demos/multichip/CR_yasui_cs.fur b/demos/multichip/CR_yasui_cs.fur new file mode 100644 index 000000000..e8a911382 Binary files /dev/null and b/demos/multichip/CR_yasui_cs.fur differ diff --git a/demos/multichip/Fiorella YM2610B+YM2203.fur b/demos/multichip/Fiorella YM2610B+YM2203.fur deleted file mode 100644 index 492f93a9f..000000000 Binary files a/demos/multichip/Fiorella YM2610B+YM2203.fur and /dev/null differ diff --git a/demos/multichip/Furnace-tan VS the spirit of the Mask.fur b/demos/multichip/Furnace-tan VS the spirit of the Mask.fur new file mode 100644 index 000000000..558ab829b Binary files /dev/null and b/demos/multichip/Furnace-tan VS the spirit of the Mask.fur differ diff --git a/demos/multichip/Melancholy_Girl_multichip.fur b/demos/multichip/Melancholy_Girl_multichip.fur new file mode 100644 index 000000000..600452328 Binary files /dev/null and b/demos/multichip/Melancholy_Girl_multichip.fur differ diff --git a/demos/multichip/Sky Chaze Zone 32X.fur b/demos/multichip/Sky Chaze Zone 32X.fur deleted file mode 100644 index 3a19b4166..000000000 Binary files a/demos/multichip/Sky Chaze Zone 32X.fur and /dev/null differ diff --git a/demos/multichip/WakeUpAndDance_MMC5_SM8521.fur b/demos/multichip/WakeUpAndDance_MMC5_SM8521.fur new file mode 100644 index 000000000..652ebb108 Binary files /dev/null and b/demos/multichip/WakeUpAndDance_MMC5_SM8521.fur differ diff --git a/demos/multichip/not genesis thing.fur b/demos/multichip/not genesis thing.fur deleted file mode 100644 index bacbfd0c5..000000000 Binary files a/demos/multichip/not genesis thing.fur and /dev/null differ diff --git a/demos/multichip/overdrive.fur b/demos/multichip/overdrive.fur deleted file mode 100644 index 7601eb52f..000000000 Binary files a/demos/multichip/overdrive.fur and /dev/null differ diff --git a/demos/nes/Bridge Zone.fur b/demos/nes/Bridge Zone.fur deleted file mode 100644 index d3843438f..000000000 Binary files a/demos/nes/Bridge Zone.fur and /dev/null differ diff --git a/demos/nes/FDS TEST.fur b/demos/nes/FDS TEST.fur deleted file mode 100644 index 5e6c15dca..000000000 Binary files a/demos/nes/FDS TEST.fur and /dev/null differ diff --git a/demos/nes/Samsung SGH-x830 - Ringtone 8.fur b/demos/nes/Samsung SGH-x830 - Ringtone 8.fur deleted file mode 100644 index df4a51a18..000000000 Binary files a/demos/nes/Samsung SGH-x830 - Ringtone 8.fur and /dev/null differ diff --git a/demos/nes/blanketcritter release ver.fur b/demos/nes/blanketcritter release ver.fur new file mode 100644 index 000000000..4a566bdc0 Binary files /dev/null and b/demos/nes/blanketcritter release ver.fur differ diff --git a/demos/opl/Air_Combat_Over_the_Vast_Mountain.fur b/demos/opl/Air_Combat_Over_the_Vast_Mountain.fur new file mode 100644 index 000000000..723c55d57 Binary files /dev/null and b/demos/opl/Air_Combat_Over_the_Vast_Mountain.fur differ diff --git a/demos/opl/One_Sided_Love_Again.fur b/demos/opl/One_Sided_Love_Again.fur new file mode 100644 index 000000000..0a05c769f Binary files /dev/null and b/demos/opl/One_Sided_Love_Again.fur differ diff --git a/demos/opl/femteknyl.fur b/demos/opl/femteknyl.fur new file mode 100644 index 000000000..333822dca Binary files /dev/null and b/demos/opl/femteknyl.fur differ diff --git a/demos/opz/'90 lost media.fur b/demos/opz/'90 lost media.fur new file mode 100644 index 000000000..522af4b9b Binary files /dev/null and b/demos/opz/'90 lost media.fur differ diff --git a/demos/opz/Clashing Angels.fur b/demos/opz/Clashing Angels.fur new file mode 100644 index 000000000..64aadba4d Binary files /dev/null and b/demos/opz/Clashing Angels.fur differ diff --git a/demos/opz/Fly to the Leaden Sky.fur b/demos/opz/Fly to the Leaden Sky.fur new file mode 100644 index 000000000..aca389fe5 Binary files /dev/null and b/demos/opz/Fly to the Leaden Sky.fur differ diff --git a/demos/opz/Glacier BM0.fur b/demos/opz/Glacier BM0.fur new file mode 100644 index 000000000..5dde66f42 Binary files /dev/null and b/demos/opz/Glacier BM0.fur differ diff --git a/demos/opz/The Hedgehog Knows One Thing.fur b/demos/opz/The Hedgehog Knows One Thing.fur new file mode 100644 index 000000000..6ed5f0a47 Binary files /dev/null and b/demos/opz/The Hedgehog Knows One Thing.fur differ diff --git a/demos/opz/The bongos and teh base OPZx2.fur b/demos/opz/The bongos and teh base OPZx2.fur new file mode 100644 index 000000000..941e4ed39 Binary files /dev/null and b/demos/opz/The bongos and teh base OPZx2.fur differ diff --git a/demos/opz/boost.fur b/demos/opz/boost.fur new file mode 100644 index 000000000..674f1cd0d Binary files /dev/null and b/demos/opz/boost.fur differ diff --git a/demos/opz/goodbye my dreams.fur b/demos/opz/goodbye my dreams.fur new file mode 100644 index 000000000..3d04bc0f0 Binary files /dev/null and b/demos/opz/goodbye my dreams.fur differ diff --git a/demos/misc/massive_x_opz.fur b/demos/opz/massive_x_opz.fur similarity index 100% rename from demos/misc/massive_x_opz.fur rename to demos/opz/massive_x_opz.fur diff --git a/demos/opz/myst_grove.fur b/demos/opz/myst_grove.fur new file mode 100644 index 000000000..c9e3acbba Binary files /dev/null and b/demos/opz/myst_grove.fur differ diff --git a/demos/opz/only_dream_opz.fur b/demos/opz/only_dream_opz.fur new file mode 100644 index 000000000..92887bb2b Binary files /dev/null and b/demos/opz/only_dream_opz.fur differ diff --git a/demos/opz/sea of crises.fur b/demos/opz/sea of crises.fur new file mode 100644 index 000000000..a772d6ddc Binary files /dev/null and b/demos/opz/sea of crises.fur differ diff --git a/demos/pce/Bad_Dudes_Greased_Lightning.fur b/demos/pce/Bad_Dudes_Greased_Lightning.fur deleted file mode 100644 index 98209900e..000000000 Binary files a/demos/pce/Bad_Dudes_Greased_Lightning.fur and /dev/null differ diff --git a/demos/pce/Contest thing 2025.fur b/demos/pce/Contest thing 2025.fur new file mode 100644 index 000000000..5bd126248 Binary files /dev/null and b/demos/pce/Contest thing 2025.fur differ diff --git a/demos/pce/Peace.fur b/demos/pce/Peace.fur new file mode 100644 index 000000000..a8ca821f4 Binary files /dev/null and b/demos/pce/Peace.fur differ diff --git a/demos/pce/Processing Plant 2025 Edit.fur b/demos/pce/Processing Plant 2025 Edit.fur new file mode 100644 index 000000000..1db8f4e78 Binary files /dev/null and b/demos/pce/Processing Plant 2025 Edit.fur differ diff --git a/demos/pce/fd_PCB.fur b/demos/pce/fd_PCB.fur new file mode 100644 index 000000000..79bf518d4 Binary files /dev/null and b/demos/pce/fd_PCB.fur differ diff --git a/demos/pce/pcengineasdmo.fur b/demos/pce/pcengineasdmo.fur new file mode 100644 index 000000000..ba8db35e4 Binary files /dev/null and b/demos/pce/pcengineasdmo.fur differ diff --git a/demos/snes/spd12x.fur b/demos/snes/spd12x.fur deleted file mode 100644 index ee4ba4d84..000000000 Binary files a/demos/snes/spd12x.fur and /dev/null differ diff --git a/demos/misc/BlueBolt_VIC20.fur b/demos/vic20/BlueBolt.fur similarity index 100% rename from demos/misc/BlueBolt_VIC20.fur rename to demos/vic20/BlueBolt.fur diff --git a/demos/vic20/h.fur b/demos/vic20/h.fur new file mode 100644 index 000000000..05243dd74 Binary files /dev/null and b/demos/vic20/h.fur differ diff --git a/demos/wonderswan/Space Station of Enormous Proportions.fur b/demos/wonderswan/Space Station of Enormous Proportions.fur new file mode 100644 index 000000000..f9b61ad15 Binary files /dev/null and b/demos/wonderswan/Space Station of Enormous Proportions.fur differ diff --git a/demos/x16/Melody of Certain Feelings.fur b/demos/x16/Melody of Certain Feelings.fur deleted file mode 100644 index eacec7a9a..000000000 Binary files a/demos/x16/Melody of Certain Feelings.fur and /dev/null differ diff --git a/demos/ymz280b/evilevilsong.fur b/demos/ymz280b/evilevilsong.fur new file mode 100644 index 000000000..4db9098cf Binary files /dev/null and b/demos/ymz280b/evilevilsong.fur differ diff --git a/demos/ymz280b/notreallyfm.fur b/demos/ymz280b/notreallyfm.fur new file mode 100644 index 000000000..f6756a6bb Binary files /dev/null and b/demos/ymz280b/notreallyfm.fur differ diff --git a/extern/imgui_software_renderer/imgui_sw.cpp b/extern/imgui_software_renderer/imgui_sw.cpp index a7471e49a..040580503 100644 --- a/extern/imgui_software_renderer/imgui_sw.cpp +++ b/extern/imgui_software_renderer/imgui_sw.cpp @@ -678,6 +678,9 @@ void ImGui_ImplSW_Shutdown() { void ImGui_ImplSW_NewFrame() { ImGui_ImplSW_Data* bd = ImGui_ImplSW_GetBackendData(); + // look I am USING THIS VARIABLE + // don't go around saying I don't use it + if (bd==NULL) abort(); IM_ASSERT(bd != nullptr); } diff --git a/src/engine/config.cpp b/src/engine/config.cpp index fe817ed2c..af4322685 100644 --- a/src/engine/config.cpp +++ b/src/engine/config.cpp @@ -58,6 +58,9 @@ bool DivConfig::save(const char* path, bool redundancy) { reportError(fmt::sprintf("could not write config file! %s",strerror(errno))); return false; } + if (redundancy) { + fputs("!DIV_CONFIG_START!\n",f); + } for (auto& i: conf) { String toWrite=fmt::sprintf("%s=%s\n",i.first,i.second); if (fwrite(toWrite.c_str(),1,toWrite.size(),f)!=toWrite.size()) { @@ -69,6 +72,9 @@ bool DivConfig::save(const char* path, bool redundancy) { return false; } } + if (redundancy) { + fputs("~DIV_CONFIG_END~\n",f); + } fclose(f); logD("config file written successfully."); return true; @@ -124,8 +130,12 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc if (redundancy) { unsigned char* readBuf=new unsigned char[CHECK_BUF_SIZE]; size_t readBufLen=0; + bool weRescued=false; for (int i=0; i0) { snprintf(line,4095,"%s.%d",path,i); } else { @@ -143,15 +153,27 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc // check whether there's something while (!feof(f)) { + bool willBreak=false; readBufLen=fread(readBuf,1,CHECK_BUF_SIZE,f); if (ferror(f)) { logV("fread(): %s",strerror(errno)); break; } + if (startCheck) { + if (readBufLen>=19) { + if (memcmp(readBuf,"!DIV_CONFIG_START!\n",19)==0) { + hasStartMarker=true; + logV("start marker found"); + } + } + startCheck=false; + } + for (size_t j=0; j=18) { + memcpy(endMarker,&readBuf[readBufLen-18],18); + } else if (readBufLen>0) { + // shift buffer left + for (size_t j=0, k=readBufLen; jDIV_PAT_VOL && (!((_x)&1))) +#define DIV_PAT_IS_EFFECT_VAL(_x) ((_x)>DIV_PAT_VOL && ((_x)&1)) + +#define DIV_NOTE_NULL_PAT 252 +#define DIV_NOTE_OFF 253 +#define DIV_NOTE_REL 254 +#define DIV_MACRO_REL 255 + // sample related #define DIV_MAX_SAMPLE_TYPE 4 diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3dd9d85c1..2c493647b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -947,8 +947,8 @@ void DivEngine::delUnusedIns() { for (int k=0; kpat[i].data[k]==NULL) continue; for (int l=0; lpatLen; l++) { - if (song.subsong[j]->pat[i].data[k]->data[l][2]>=0 && song.subsong[j]->pat[i].data[k]->data[l][2]<256) { - isUsed[song.subsong[j]->pat[i].data[k]->data[l][2]]=true; + if (song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]>=0 && song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]<256) { + isUsed[song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]]=true; } } } @@ -1033,38 +1033,6 @@ void DivEngine::delUnusedSamples() { } } - // scan in pattern (legacy sample mode) - // disabled because it is unreliable - /* - for (DivSubSong* i: song.subsong) { - for (int j=0; jordersLen; k++) { - DivPattern* p=i->pat[j].getPattern(i->orders.ord[j][k],false); - for (int l=0; lpatLen; l++) { - for (int m=0; mpat[j].effectCols; m++) { - if (p->data[l][4+(m<<1)]==0x17) { - is17On=(p->data[l][5+(m<<1)]>0); - } - if (p->data[l][4+(m<<1)]==0xeb) { - bank=p->data[l][5+(m<<1)]; - if (bank==-1) bank=0; - } - } - if (is17On) { - if (p->data[l][1]!=0 || p->data[l][0]!=0) { - if (p->data[l][0]<=12) { - int note=(12*bank)+(p->data[l][0]%12); - if (note<256) isUsed[note]=true; - } - } - } - } - } - } - }*/ - // delete for (int i=0; i=180) { + return DIV_NOTE_NULL_PAT; + } else { + return seek; + } + } + + return -1; +} + +void DivEngine::noteToSplitNote(short note, short& outNote, short& outOctave) { + switch (note) { + case DIV_NOTE_OFF: + outNote=100; + outOctave=0; + break; + case DIV_NOTE_REL: + outNote=101; + outOctave=0; + break; + case DIV_MACRO_REL: + outNote=102; + outOctave=0; + break; + case DIV_NOTE_NULL_PAT: + // "BUG" note! + outNote=0; + outOctave=1; + break; + case -1: + outNote=0; + outOctave=0; + break; + default: + outNote=note%12; + outOctave=(unsigned char)(note-60)/12; + if (outNote==0) { + outNote=12; + outOctave--; + } + break; + } +} + void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) { BUSY_BEGIN; previewSampleNoLock(sample,note,pStart,pEnd); @@ -2749,8 +2775,8 @@ void DivEngine::delInstrumentUnsafe(int index) { for (int k=0; kpat[i].data[k]==NULL) continue; for (int l=0; lpatLen; l++) { - if (song.subsong[j]->pat[i].data[k]->data[l][2]>index) { - song.subsong[j]->pat[i].data[k]->data[l][2]--; + if (song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]>index) { + song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]--; } } } @@ -3111,7 +3137,7 @@ void DivEngine::deepCloneOrder(int pos, bool where) { order[i]=j; DivPattern* oldPat=curPat[i].getPattern(origOrd,false); DivPattern* pat=curPat[i].getPattern(j,true); - memcpy(pat->data,oldPat->data,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short)); + memcpy(pat->newData,oldPat->newData,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short)); logD("found at %d",j); didNotFind=false; break; @@ -3217,10 +3243,10 @@ void DivEngine::exchangeIns(int one, int two) { for (int k=0; kpat[i].data[k]==NULL) continue; for (int l=0; lpatLen; l++) { - if (song.subsong[j]->pat[i].data[k]->data[l][2]==one) { - song.subsong[j]->pat[i].data[k]->data[l][2]=two; - } else if (song.subsong[j]->pat[i].data[k]->data[l][2]==two) { - song.subsong[j]->pat[i].data[k]->data[l][2]=one; + if (song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]==one) { + song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]=two; + } else if (song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]==two) { + song.subsong[j]->pat[i].data[k]->newData[l][DIV_PAT_INS]=one; } } } diff --git a/src/engine/engine.h b/src/engine/engine.h index b4dc29a75..86993b69b 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -54,8 +54,8 @@ class DivWorkPool; #define DIV_UNSTABLE -#define DIV_VERSION "dev233" -#define DIV_ENGINE_VERSION 233 +#define DIV_VERSION "dev235" +#define DIV_ENGINE_VERSION 235 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -140,7 +140,7 @@ struct DivChannelState { int panDepth, panRate, panPos, panSpeed; int sampleOff; unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta, cutType; - bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff, releasing; + bool doNote, legato, portaStop, keyOn, keyOff, stopOnOff, releasing; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp, sampleOffSet; bool wentThroughNote, goneThroughNote; @@ -197,7 +197,6 @@ struct DivChannelState { portaStop(false), keyOn(false), keyOff(false), - nowYouCanStop(true), stopOnOff(false), releasing(false), arpYield(false), @@ -723,7 +722,7 @@ class DivEngine { SafeWriter* saveDMF(unsigned char version); // save as .fur. // if notPrimary is true then the song will not be altered - SafeWriter* saveFur(bool notPrimary=false, bool newPatternFormat=true); + SafeWriter* saveFur(bool notPrimary=false); // return a ROM exporter. DivROMExport* buildROM(DivROMExportOptions sys); // dump to VGM. @@ -929,6 +928,10 @@ class DivEngine { // get effective sample rate int getEffectiveSampleRate(int rate); + // convert between old and new note/octave format + short splitNoteToNote(short note, short octave); + void noteToSplitNote(short note, short& outNote, short& outOctave); + // is FM system bool isFMSystem(DivSystem sys); diff --git a/src/engine/fileOps/dmf.cpp b/src/engine/fileOps/dmf.cpp index 910be0d1e..2b43ce275 100644 --- a/src/engine/fileOps/dmf.cpp +++ b/src/engine/fileOps/dmf.cpp @@ -783,109 +783,113 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { DivPattern* pat=chan.getPattern(ds.subsong[0]->orders.ord[i][j],true); if (ds.version>0x08) { // current pattern format for (int k=0; kpatLen; k++) { - // note - pat->data[k][0]=reader.readS(); - // octave - pat->data[k][1]=reader.readS(); - if (ds.system[0]==DIV_SYSTEM_SMS && ds.version<0x0e && pat->data[k][1]>0) { + short note=reader.readS(); + short octave=reader.readS(); + if (ds.system[0]==DIV_SYSTEM_SMS && ds.version<0x0e && octave>0) { // apparently it was up one octave before - pat->data[k][1]--; - } else if (ds.system[0]==DIV_SYSTEM_GENESIS && ds.version<0x0e && pat->data[k][1]>0 && i>5) { + octave--; + } else if (ds.system[0]==DIV_SYSTEM_GENESIS && ds.version<0x0e && octave>0 && i>5) { // ditto - pat->data[k][1]--; - } else if (ds.system[0]==DIV_SYSTEM_MSX2 && pat->data[k][1]>0 && i<3) { + octave--; + } else if (ds.system[0]==DIV_SYSTEM_MSX2 && octave>0 && i<3) { // why the hell? - pat->data[k][1]++; + octave++; } if (ds.version<0x12) { - if (ds.system[0]==DIV_SYSTEM_GB && i==3 && pat->data[k][1]>0) { + if (ds.system[0]==DIV_SYSTEM_GB && i==3 && octave>0) { // back then noise was 2 octaves lower - pat->data[k][1]-=2; + octave-=2; } } - if (ds.system[0]==DIV_SYSTEM_YMU759 && pat->data[k][0]!=0) { + if (ds.system[0]==DIV_SYSTEM_YMU759 && note!=0) { // apparently YMU759 is stored 2 octaves lower - pat->data[k][1]+=2; + octave+=2; } - if (pat->data[k][0]==0 && pat->data[k][1]!=0) { - logD("what? %d:%d:%d note %d octave %d",i,j,k,pat->data[k][0],pat->data[k][1]); - pat->data[k][0]=12; - pat->data[k][1]--; + if (note==0 && octave!=0) { + logD("what? %d:%d:%d note %d octave %d",i,j,k,note,octave); + note=12; + octave--; } + + pat->newData[k][DIV_PAT_NOTE]=splitNoteToNote(note,octave); + // volume - pat->data[k][3]=reader.readS(); + pat->newData[k][DIV_PAT_VOL]=reader.readS(); if (ds.version<0x0a) { // back then volume was stored as 00-ff instead of 00-7f/0-f if (i>5) { - pat->data[k][3]>>=4; + pat->newData[k][DIV_PAT_VOL]>>=4; } else { - pat->data[k][3]>>=1; + pat->newData[k][DIV_PAT_VOL]>>=1; } } if (ds.version<0x12) { - if (ds.system[0]==DIV_SYSTEM_GB && i==2 && pat->data[k][3]>0) { + if (ds.system[0]==DIV_SYSTEM_GB && i==2 && pat->newData[k][DIV_PAT_VOL]>0) { // volume range of GB wave channel was 0-3 rather than 0-F - pat->data[k][3]=(pat->data[k][3]&3)*5; + pat->newData[k][DIV_PAT_VOL]=(pat->newData[k][DIV_PAT_VOL]&3)*5; } } for (int l=0; ldata[k][4+(l<<1)]=reader.readS(); - pat->data[k][5+(l<<1)]=reader.readS(); + pat->newData[k][DIV_PAT_FX(l)]=reader.readS(); + pat->newData[k][DIV_PAT_FXVAL(l)]=reader.readS(); if (ds.version<0x14) { - if (pat->data[k][4+(l<<1)]==0xe5 && pat->data[k][5+(l<<1)]!=-1) { - pat->data[k][5+(l<<1)]=128+((pat->data[k][5+(l<<1)]-128)/4); + // the range of E5xx was different back then + if (pat->newData[k][DIV_PAT_FX(l)]==0xe5 && pat->newData[k][DIV_PAT_FXVAL(l)]!=-1) { + pat->newData[k][DIV_PAT_FXVAL(l)]=128+((pat->newData[k][DIV_PAT_FXVAL(l)]-128)/4); } } } // instrument - pat->data[k][2]=reader.readS(); + pat->newData[k][DIV_PAT_INS]=reader.readS(); // this is sad if (ds.system[0]==DIV_SYSTEM_NES_FDS) { - if (i==5 && pat->data[k][2]!=-1) { - if (pat->data[k][2]>=0 && pat->data[k][2]data[k][2]]->type=DIV_INS_FDS; + if (i==5 && pat->newData[k][DIV_PAT_INS]!=-1) { + if (pat->newData[k][DIV_PAT_INS]>=0 && pat->newData[k][DIV_PAT_INS]newData[k][DIV_PAT_INS]]->type=DIV_INS_FDS; } } } if (ds.system[0]==DIV_SYSTEM_MSX2) { - if (i>=3 && pat->data[k][2]!=-1) { - if (pat->data[k][2]>=0 && pat->data[k][2]data[k][2]]->type=DIV_INS_SCC; + if (i>=3 && pat->newData[k][DIV_PAT_INS]!=-1) { + if (pat->newData[k][DIV_PAT_INS]>=0 && pat->newData[k][DIV_PAT_INS]newData[k][DIV_PAT_INS]]->type=DIV_INS_SCC; } } } } } else { // historic pattern format - if (i<16) pat->data[0][2]=historicColIns[i]; + if (i<16) pat->newData[0][DIV_PAT_INS]=historicColIns[i]; for (int k=0; kpatLen; k++) { - // note - pat->data[k][0]=reader.readC(); - // octave - pat->data[k][1]=reader.readC(); - if (pat->data[k][0]!=0) { + short note=reader.readC(); + short octave=reader.readC(); + + if (note!=0) { // YMU759 is stored 2 octaves lower - pat->data[k][1]+=2; + octave+=2; } - if (pat->data[k][0]==0 && pat->data[k][1]!=0) { - logD("what? %d:%d:%d note %d octave %d",i,j,k,pat->data[k][0],pat->data[k][1]); - pat->data[k][0]=12; - pat->data[k][1]--; + if (note==0 && octave!=0) { + logD("what? %d:%d:%d note %d octave %d",i,j,k,note,octave); + note=12; + octave--; } + + pat->newData[k][DIV_PAT_NOTE]=splitNoteToNote(note,octave); + // volume and effect unsigned char vol=reader.readC(); unsigned char fx=reader.readC(); unsigned char fxVal=reader.readC(); - pat->data[k][3]=(vol==0x80 || vol==0xff)?-1:vol; + pat->newData[k][DIV_PAT_VOL]=(vol==0x80 || vol==0xff)?-1:vol; // effect - pat->data[k][4]=(fx==0x80 || fx==0xff)?-1:fx; - pat->data[k][5]=(fxVal==0x80 || fx==0xff)?-1:fxVal; + pat->newData[k][DIV_PAT_FX(0)]=(fx==0x80 || fx==0xff)?-1:fx; + pat->newData[k][DIV_PAT_FXVAL(0)]=(fxVal==0x80 || fx==0xff)?-1:fxVal; // instrument if (ds.version>0x05) { - pat->data[k][2]=reader.readC(); - if (pat->data[k][2]==0x80 || pat->data[k][2]==0xff) pat->data[k][2]=-1; + pat->newData[k][DIV_PAT_INS]=reader.readC(); + if (pat->newData[k][DIV_PAT_INS]==0x80 || pat->newData[k][DIV_PAT_INS]==0xff) pat->newData[k][DIV_PAT_INS]=-1; } } } @@ -1630,12 +1634,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { bool relWarning=false; for (int i=0; iwriteC(curPat[i].effectCols); for (int j=0; jordersLen; j++) { DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][j],false); for (int k=0; kpatLen; k++) { - if ((pat->data[k][0]==101 || pat->data[k][0]==102) && pat->data[k][1]==0) { + if (pat->newData[k][DIV_PAT_NOTE]==DIV_NOTE_REL || pat->newData[k][DIV_PAT_NOTE]==DIV_MACRO_REL) { w->writeS(100); w->writeS(0); if (!relWarning) { @@ -1643,18 +1648,19 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { addWarning("note/macro release will be converted to note off!"); } } else { - w->writeS(pat->data[k][0]); // note - w->writeS(pat->data[k][1]); // octave + noteToSplitNote(pat->newData[k][DIV_PAT_NOTE],note,octave); + w->writeS(note); // note + w->writeS(octave); // octave } - w->writeS(pat->data[k][3]); // volume + w->writeS(pat->newData[k][DIV_PAT_VOL]); // volume #ifdef TA_BIG_ENDIAN for (int l=0; lwriteS(pat->data[k][4+l]); + w->writeS(pat->newData[k][DIV_PAT_FX(0)+l]); } #else - w->write(&pat->data[k][4],2*curPat[i].effectCols*2); // effects + w->write(&pat->newData[k][DIV_PAT_FX(0)],2*curPat[i].effectCols*2); // effects #endif - w->writeS(pat->data[k][2]); // instrument + w->writeS(pat->newData[k][DIV_PAT_INS]); // instrument } } } diff --git a/src/engine/fileOps/fc.cpp b/src/engine/fileOps/fc.cpp index da57fad69..6e4408a7d 100644 --- a/src/engine/fileOps/fc.cpp +++ b/src/engine/fileOps/fc.cpp @@ -418,8 +418,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) { ds.subsong[0]->orders.ord[j][i]=i; DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true); if (j==3 && seq[i].speed) { - p->data[0][6]=0x0f; - p->data[0][7]=seq[i].speed; + p->newData[0][DIV_PAT_FX(1)]=0x0f; + p->newData[0][DIV_PAT_FXVAL(1)]=seq[i].speed; } bool ignoreNext=false; @@ -428,80 +428,65 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) { FCPattern& fp=pat[seq[i].pat[j]]; if (fp.note[k]>0 && fp.note[k]<0x49) { lastNote[j]=fp.note[k]; - short note=(fp.note[k]+seq[i].transpose[j])%12; - short octave=2+((fp.note[k]+seq[i].transpose[j])/12); - if (fp.note[k]>=0x3d) octave-=6; - if (note==0) { - note=12; - octave--; - } - octave&=0xff; - p->data[k][0]=note; - p->data[k][1]=octave; + p->newData[k][DIV_PAT_NOTE]=fp.note[k]+seq[i].transpose[j]+84; + // wrap-around if the note is too high + if (fp.note[k]>=0x3d) p->newData[k][DIV_PAT_NOTE]-=6*12; if (isSliding[j]) { isSliding[j]=false; - p->data[k][4]=2; - p->data[k][5]=0; + p->newData[k][DIV_PAT_FX(0)]=2; + p->newData[k][DIV_PAT_FXVAL(0)]=0; } } else if (fp.note[k]==0x49) { if (k>0) { - p->data[k-1][4]=0x0d; - p->data[k-1][5]=0; + p->newData[k-1][DIV_PAT_FX(0)]=0x0d; + p->newData[k-1][DIV_PAT_FXVAL(0)]=0; } } else if (k==0 && lastTranspose[j]!=seq[i].transpose[j]) { - p->data[0][2]=lastIns[j]; - p->data[0][4]=0x03; - p->data[0][5]=0xff; + p->newData[0][DIV_PAT_INS]=lastIns[j]; + p->newData[0][DIV_PAT_FX(0)]=0x03; + p->newData[0][DIV_PAT_FXVAL(0)]=0xff; lastTranspose[j]=seq[i].transpose[j]; - short note=(lastNote[j]+seq[i].transpose[j])%12; - short octave=2+((lastNote[j]+seq[i].transpose[j])/12); - if (lastNote[j]>=0x3d) octave-=6; - if (note==0) { - note=12; - octave--; - } - octave&=0xff; - p->data[k][0]=note; - p->data[k][1]=octave; + p->newData[k][DIV_PAT_NOTE]=lastNote[j]+seq[i].transpose[j]+84; + // wrap-around if the note is too high + if (lastNote[j]>=0x3d) p->newData[k][DIV_PAT_NOTE]-=6*12; } if (fp.val[k]) { if (ignoreNext) { ignoreNext=false; } else { if (fp.val[k]==0xf0) { - p->data[k][0]=100; - p->data[k][1]=0; - p->data[k][2]=-1; + p->newData[k][DIV_PAT_NOTE]=DIV_NOTE_OFF; + p->newData[k][DIV_PAT_INS]=-1; } else if (fp.val[k]&0xe0) { if (fp.val[k]&0x40) { - p->data[k][4]=2; - p->data[k][5]=0; + p->newData[k][DIV_PAT_FX(0)]=2; + p->newData[k][DIV_PAT_FXVAL(0)]=0; isSliding[j]=false; } else if (fp.val[k]&0x80) { isSliding[j]=true; if (k<31) { if (fp.val[k+1]&0x20) { - p->data[k][4]=2; - p->data[k][5]=fp.val[k+1]&0x1f; + p->newData[k][DIV_PAT_FX(0)]=2; + p->newData[k][DIV_PAT_FXVAL(0)]=fp.val[k+1]&0x1f; } else { - p->data[k][4]=1; - p->data[k][5]=fp.val[k+1]&0x1f; + p->newData[k][DIV_PAT_FX(0)]=1; + p->newData[k][DIV_PAT_FXVAL(0)]=fp.val[k+1]&0x1f; } ignoreNext=true; } else { - p->data[k][4]=2; - p->data[k][5]=0; + p->newData[k][DIV_PAT_FX(0)]=2; + p->newData[k][DIV_PAT_FXVAL(0)]=0; } } } else { - p->data[k][2]=(fp.val[k]+seq[i].offsetIns[j])&0x3f; - lastIns[j]=p->data[k][2]; + p->newData[k][DIV_PAT_INS]=(fp.val[k]+seq[i].offsetIns[j])&0x3f; + lastIns[j]=p->newData[k][DIV_PAT_INS]; } } } else if (fp.note[k]>0 && fp.note[k]<0x49) { - p->data[k][2]=seq[i].offsetIns[j]; - lastIns[j]=p->data[k][2]; + p->newData[k][DIV_PAT_INS]=seq[i].offsetIns[j]; + lastIns[j]=p->newData[k][DIV_PAT_INS]; } } } diff --git a/src/engine/fileOps/ftm.cpp b/src/engine/fileOps/ftm.cpp index b471959d6..d1a59c07a 100644 --- a/src/engine/fileOps/ftm.cpp +++ b/src/engine/fileOps/ftm.cpp @@ -270,7 +270,7 @@ int convert_vrc6_duties[4] = {1, 3, 7, 3}; int findEmptyFx(short* data) { for (int i=0; i<7; i++) { - if (data[4+i*2]==-1) return i; + if (data[DIV_PAT_FX(i)]==-1) return i; } return -1; } @@ -1752,17 +1752,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (map_channels[ch] != 0xff) { if (nextNote == 0x0d) { - pat->data[row][0] = 101; + pat->newData[row][DIV_PAT_NOTE] = DIV_NOTE_REL; } else if (nextNote == 0x0e) { - pat->data[row][0] = 100; - } else if (nextNote == 0x01) { - pat->data[row][0] = 12; - pat->data[row][1] = nextOctave - 1; + pat->newData[row][DIV_PAT_NOTE] = DIV_NOTE_OFF; } else if (nextNote == 0) { - pat->data[row][0] = 0; + pat->newData[row][DIV_PAT_NOTE] = -1; } else if (nextNote < 0x0d) { - pat->data[row][0] = nextNote - 1; - pat->data[row][1] = nextOctave; + pat->newData[row][DIV_PAT_NOTE] = nextOctave*12 + (nextNote - 1) + 60; } } @@ -1770,27 +1766,27 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si // TODO: you sure about 0xff? if (map_channels[ch] != 0xff) { if (nextIns < 0x40 && nextNote != 0x0d && nextNote != 0x0e) { - pat->data[row][2] = nextIns; + pat->newData[row][DIV_PAT_INS] = nextIns; } else { - pat->data[row][2] = -1; + pat->newData[row][DIV_PAT_INS] = -1; } } unsigned char nextVol = reader.readC(); if (map_channels[ch] != 0xff) { if (nextVol < 0x10) { - pat->data[row][3] = nextVol; + pat->newData[row][DIV_PAT_VOL] = nextVol; if (map_channels[ch] == vrc6_saw_chan) // scale volume { // TODO: shouldn't it be 32? - pat->data[row][3] = (pat->data[row][3] * 42) / 15; + pat->newData[row][DIV_PAT_VOL] = (pat->newData[row][DIV_PAT_VOL] * 42) / 15; } if (map_channels[ch] == fds_chan) { - pat->data[row][3] = (pat->data[row][3] * 31) / 15; + pat->newData[row][DIV_PAT_VOL] = (pat->newData[row][DIV_PAT_VOL] * 31) / 15; } } else { - pat->data[row][3] = -1; + pat->newData[row][DIV_PAT_VOL] = -1; } } @@ -1829,15 +1825,15 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (nextEffect == FT_EF_SPEED && nextEffectVal < 20) nextEffectVal++; - if (pat->data[row][3] == 0) - pat->data[row][3] = 0xf; + if (pat->newData[row][DIV_PAT_VOL] == 0) + pat->newData[row][DIV_PAT_VOL] = 0xf; else { - pat->data[row][3]--; - pat->data[row][3] &= 0x0F; + pat->newData[row][DIV_PAT_VOL]--; + pat->newData[row][DIV_PAT_VOL] &= 0x0F; } - if (pat->data[row][0] == 0) - pat->data[row][2] = -1; + if (pat->newData[row][DIV_PAT_NOTE] == -1) + pat->newData[row][DIV_PAT_INS] = -1; } if (blockVersion == 3) { @@ -1902,43 +1898,43 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (map_channels[ch] != 0xff) { if (nextEffect == 0 && nextEffectVal == 0) { - pat->data[row][4 + (j * 2)] = -1; - pat->data[row][5 + (j * 2)] = -1; + pat->newData[row][DIV_PAT_FX(j)] = -1; + pat->newData[row][DIV_PAT_FXVAL(j)] = -1; } else { if ((eft && nextEffectdata[row][4 + (j * 2)] = eftEffectMap[nextEffect]; - pat->data[row][5 + (j * 2)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal; + pat->newData[row][DIV_PAT_FX(j)] = eftEffectMap[nextEffect]; + pat->newData[row][DIV_PAT_FXVAL(j)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal; - if (pat->data[row][4 + (j * 2)] == 0x100) { - pat->data[row][3] += pat->data[row][5 + (j * 2)] ? 0x10 : 0; // extra volume bit for AY8930 - pat->data[row][4 + (j * 2)] = -1; - pat->data[row][5 + (j * 2)] = -1; + if (pat->newData[row][DIV_PAT_FX(j)] == 0x100) { + pat->newData[row][DIV_PAT_VOL] += pat->newData[row][DIV_PAT_FXVAL(j)] ? 0x10 : 0; // extra volume bit for AY8930 + pat->newData[row][DIV_PAT_FX(j)] = -1; + pat->newData[row][DIV_PAT_FXVAL(j)] = -1; } if (eftEffectMap[nextEffect] == 0x0f && nextEffectVal > 0x1f) { - pat->data[row][4 + (j * 2)] = 0xfd; // BPM speed change! + pat->newData[row][DIV_PAT_FX(j)] = 0xfd; // BPM speed change! } if ((eftEffectMap[nextEffect] == 0xe1 || eftEffectMap[nextEffect] == 0xe2) && (nextEffectVal & 0xf0) == 0) { - pat->data[row][5 + (j * 2)] |= 0x10; // in FamiTracker if e1/e2 commands speed is 0 the portamento still has some speed! + pat->newData[row][DIV_PAT_FXVAL(j)] |= 0x10; // in FamiTracker if e1/e2 commands speed is 0 the portamento still has some speed! } } else { - pat->data[row][4 + (j * 2)] = ftEffectMap[nextEffect]; - pat->data[row][5 + (j * 2)] = ftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal; + pat->newData[row][DIV_PAT_FX(j)] = ftEffectMap[nextEffect]; + pat->newData[row][DIV_PAT_FXVAL(j)] = ftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal; if (ftEffectMap[nextEffect] == 0x0f && nextEffectVal > 0x1f) { - pat->data[row][4 + (j * 2)] = 0xfd; // BPM speed change! + pat->newData[row][DIV_PAT_FX(j)] = 0xfd; // BPM speed change! } if ((ftEffectMap[nextEffect] == 0xe1 || ftEffectMap[nextEffect] == 0xe2) && (nextEffectVal & 0xf0) == 0) { - pat->data[row][5 + (j * 2)] |= 0x10; // in FamiTracker if e1/e2 commands speed is 0 the portamento still has some speed! + pat->newData[row][DIV_PAT_FXVAL(j)] |= 0x10; // in FamiTracker if e1/e2 commands speed is 0 the portamento still has some speed! } } for (int v = 0; v < 8; v++) { if (map_channels[ch] == n163_chans[v]) { - if (pat->data[row][4 + (j * 2)] == 0x12) { - pat->data[row][4 + (j * 2)] = 0x110; // N163 wave change (we'll map this later) + if (pat->newData[row][DIV_PAT_FX(j)] == 0x12) { + pat->newData[row][DIV_PAT_FX(j)] = 0x110; // N163 wave change (we'll map this later) } } } @@ -1947,23 +1943,24 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si { if (map_channels[ch] == vrc7_chans[vrr]) { - if (pat->data[row][4 + (j * 2)] == 0x12) + if (pat->newData[row][DIV_PAT_FX(j)] == 0x12) { - pat->data[row][4 + (j * 2)] = 0x10; // set VRC7 patch + pat->newData[row][DIV_PAT_FX(j)] = 0x10; // set VRC7 patch } } } for (int v = 0; v < 3; v++) { if (map_channels[ch] == s5b_chans[v] || map_channels[ch] == ay8930_chans[v]) { - if (pat->data[row][4 + (j * 2)] == 0x22 && (pat->data[row][5 + (j * 2)] & 0xf0) != 0) { - pat->data[row][4 + (7 * 2)] = -666; //marker + if (pat->newData[row][DIV_PAT_FX(j)] == 0x22 && (pat->newData[row][DIV_PAT_FXVAL(j)] & 0xf0) != 0) { + // TODO: in the second stage of pattern refactor this will have to change. + pat->newData[row][DIV_PAT_FX(7)] = -666; //marker } } } } else { - pat->data[row][4 + (j * 2)] = -1; - pat->data[row][5 + (j * 2)] = -1; + pat->newData[row][DIV_PAT_FX(j)] = -1; + pat->newData[row][DIV_PAT_FXVAL(j)] = -1; } } } @@ -2438,8 +2435,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (ds.subsong[j]->pat[ii].data[k] == NULL) continue; for (int l = 0; l < ds.subsong[j]->patLen; l++) { - if (ds.subsong[j]->pat[ii].data[k]->data[l][2] > index) { - ds.subsong[j]->pat[ii].data[k]->data[l][2]--; + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] > index) { + ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS]--; } } } @@ -2456,12 +2453,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (ds.subsong[j]->pat[ii].data[k] == NULL) continue; for (int l = 0; l < ds.subsong[j]->patLen; l++) { - if (ds.subsong[j]->pat[ii].data[k]->data[l][4 + 7*2] == -666) { + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FX(7)] == -666) { bool converted = false; // for()? if()? THESE ARE NOT FUNCTIONS! for (int hh = 0; hh < 7; hh++) { // oh and now you 1TBS up. oh man... - if (ds.subsong[j]->pat[ii].data[k]->data[l][4 + hh*2] == 0x22 && !converted) { - int slot = findEmptyFx(ds.subsong[j]->pat[ii].data[k]->data[l]); + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FX(hh)] == 0x22 && !converted) { + int slot = findEmptyFx(ds.subsong[j]->pat[ii].data[k]->newData[l]); if (slot != -1) { // space your comments damn it! // Hxy - Envelope automatic pitch @@ -2469,7 +2466,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si // Sets envelope period to the note period shifted by x and envelope type y. // Approximate envelope frequency is note frequency * (2^|x - 8|) / 32. - int ftAutoEnv = (ds.subsong[j]->pat[ii].data[k]->data[l][5 + hh*2] >> 4) & 15; + int ftAutoEnv = (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FXVAL(hh)] >> 4) & 15; int autoEnvDen = 16; // ???? with 32 it's an octave lower... int autoEnvNum = (1 << (abs(ftAutoEnv - 8))); @@ -2479,18 +2476,18 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } if (autoEnvDen < 16 && autoEnvNum < 16) { - ds.subsong[j]->pat[ii].data[k]->data[l][4 + slot*2] = 0x29; - ds.subsong[j]->pat[ii].data[k]->data[l][5 + slot*2] = (autoEnvNum << 4) | autoEnvDen; + ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FX(slot)] = 0x29; + ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FXVAL(slot)] = (autoEnvNum << 4) | autoEnvDen; } - ds.subsong[j]->pat[ii].data[k]->data[l][5 + hh*2] = (ds.subsong[j]->pat[ii].data[k]->data[l][5 + hh*2] & 0xf) << 4; + ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FXVAL(hh)] = (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FXVAL(hh)] & 0xf) << 4; converted = true; } } } - ds.subsong[j]->pat[ii].data[k]->data[l][4 + (7 * 2)] = -1; //delete marker + ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_FX(7)] = -1; //delete marker } } } @@ -2506,10 +2503,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si for (int p = 0; p < s->ordersLen; p++) { for (int r = 0; r < s->patLen; r++) { DivPattern* pat = s->pat[c].getPattern(s->orders.ord[c][p], true); - short* s_row_data = pat->data[r]; + short* s_row_data = pat->newData[r]; for (int eff = 0; eff < DIV_MAX_EFFECTS - 1; eff++) { - if (s_row_data[4 + 2 * eff] != -1 && eff + 1 > num_fx) { + if (s_row_data[DIV_PAT_FX(eff)] != -1 && eff + 1 > num_fx) { num_fx = eff + 1; } } @@ -2556,7 +2553,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si continue; for (int l = 0; l < ds.subsong[j]->patLen; l++) { // 1TBS > GNU - if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == i) { // instrument + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == i) { // instrument DivInstrument* ins = ds.ins[i]; bool go_to_end = false; @@ -2606,7 +2603,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (ds.subsong[j]->pat[ii].data[k] == NULL) continue; for (int l = 0; l < ds.subsong[j]->patLen; l++) { - if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == i) // instrument + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == i) // instrument { DivInstrument* ins = ds.ins[i]; bool go_to_end = false; @@ -2658,7 +2655,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (ds.subsong[j]->pat[ii].data[k] == NULL) continue; for (int l = 0; l < ds.subsong[j]->patLen; l++) { - if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == i) // instrument + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == i) // instrument { DivInstrument* ins = ds.ins[i]; bool go_to_end = false; @@ -2729,17 +2726,17 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (ds.subsong[j]->pat[ii].data[k] == NULL) continue; for (int l = 0; l < ds.subsong[j]->patLen; l++) { - if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == ins_vrc6_conv[i][0] && (ii == vrc6_chans[0] || ii == vrc6_chans[1])) // change ins index + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == ins_vrc6_conv[i][0] && (ii == vrc6_chans[0] || ii == vrc6_chans[1])) // change ins index { - ds.subsong[j]->pat[ii].data[k]->data[l][2] = ins_vrc6_conv[i][1]; + ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] = ins_vrc6_conv[i][1]; } - if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == ins_vrc6_saw_conv[i][0] && ii == vrc6_saw_chan) { - ds.subsong[j]->pat[ii].data[k]->data[l][2] = ins_vrc6_saw_conv[i][1]; + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == ins_vrc6_saw_conv[i][0] && ii == vrc6_saw_chan) { + ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] = ins_vrc6_saw_conv[i][1]; } - if (ds.subsong[j]->pat[ii].data[k]->data[l][2] == ins_nes_conv[i][0] && (ii == mmc5_chans[0] || ii == mmc5_chans[1] || ii < 5)) { - ds.subsong[j]->pat[ii].data[k]->data[l][2] = ins_nes_conv[i][1]; + if (ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] == ins_nes_conv[i][0] && (ii == mmc5_chans[0] || ii == mmc5_chans[1] || ii < 5)) { + ds.subsong[j]->pat[ii].data[k]->newData[l][DIV_PAT_INS] = ins_nes_conv[i][1]; } } } @@ -2785,19 +2782,19 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si DivPattern* p=i->pat[j].getPattern(i->orders.ord[j][k],true); for (int l=0; lpatLen; l++) { // check for instrument change - if (p->data[l][2]!=-1) { - curWaveOff=n163WaveOff[p->data[l][2]&127]; + if (p->newData[l][DIV_PAT_INS]!=-1) { + curWaveOff=n163WaveOff[p->newData[l][DIV_PAT_INS]&127]; } // check effect columns for 0x110 (dummy wave change) for (int m=0; mpat[j].effectCols; m++) { - if (p->data[l][4+(m<<1)]==0x110) { + if (p->newData[l][DIV_PAT_FX(m)]==0x110) { // map wave - p->data[l][4+(m<<1)]=0x10; - if (p->data[l][5+(m<<1)]==-1) { - p->data[l][5+(m<<1)]=curWaveOff&0xff; + p->newData[l][DIV_PAT_FX(m)]=0x10; + if (p->newData[l][DIV_PAT_FXVAL(m)]==-1) { + p->newData[l][DIV_PAT_FXVAL(m)]=curWaveOff&0xff; } else { - p->data[l][5+(m<<1)]=(p->data[l][5+(m<<1)]+curWaveOff)&0xff; + p->newData[l][DIV_PAT_FXVAL(m)]=(p->newData[l][DIV_PAT_FXVAL(m)]+curWaveOff)&0xff; } } } diff --git a/src/engine/fileOps/fur.cpp b/src/engine/fileOps/fur.cpp index e67360108..e4b8c70d2 100644 --- a/src/engine/fileOps/fur.cpp +++ b/src/engine/fileOps/fur.cpp @@ -1778,32 +1778,28 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) { if (mask&1) { // note unsigned char note=reader.readC(); + // TODO: PAT2 format with new off/===/rel values! if (note==180) { - pat->data[j][0]=100; - pat->data[j][1]=0; + pat->newData[j][0]=DIV_NOTE_OFF; } else if (note==181) { - pat->data[j][0]=101; - pat->data[j][1]=0; + pat->newData[j][0]=DIV_NOTE_REL; } else if (note==182) { - pat->data[j][0]=102; - pat->data[j][1]=0; + pat->newData[j][0]=DIV_MACRO_REL; } else if (note<180) { - pat->data[j][0]=newFormatNotes[note]; - pat->data[j][1]=newFormatOctaves[note]; + pat->newData[j][DIV_PAT_NOTE]=note; } else { - pat->data[j][0]=0; - pat->data[j][1]=0; + pat->newData[j][0]=-1; } } if (mask&2) { // instrument - pat->data[j][2]=(unsigned char)reader.readC(); + pat->newData[j][DIV_PAT_INS]=(unsigned char)reader.readC(); } if (mask&4) { // volume - pat->data[j][3]=(unsigned char)reader.readC(); + pat->newData[j][DIV_PAT_VOL]=(unsigned char)reader.readC(); } for (unsigned char k=0; k<16; k++) { if (effectMask&(1<data[j][4+k]=(unsigned char)reader.readC(); + pat->newData[j][DIV_PAT_FX(0)+k]=(unsigned char)reader.readC(); } } } @@ -1844,18 +1840,20 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) { DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true); for (int j=0; jpatLen; j++) { - pat->data[j][0]=reader.readS(); - pat->data[j][1]=reader.readS(); - pat->data[j][2]=reader.readS(); - pat->data[j][3]=reader.readS(); - for (int k=0; kpat[chan].effectCols; k++) { - pat->data[j][4+(k<<1)]=reader.readS(); - pat->data[j][5+(k<<1)]=reader.readS(); + short note=reader.readS(); + short octave=reader.readS(); + if (note==0 && octave!=0) { + logD("what? %d:%d:%d note %d octave %d",chan,i,j,note,octave); + note=12; + octave--; } - if (pat->data[j][0]==0 && pat->data[j][1]!=0) { - logD("what? %d:%d:%d note %d octave %d",chan,i,j,pat->data[j][0],pat->data[j][1]); - pat->data[j][0]=12; - pat->data[j][1]--; + pat->newData[j][DIV_PAT_NOTE]=splitNoteToNote(note,octave); + + pat->newData[j][DIV_PAT_INS]=reader.readS(); + pat->newData[j][DIV_PAT_VOL]=reader.readS(); + for (int k=0; kpat[chan].effectCols; k++) { + pat->newData[j][DIV_PAT_FX(k)]=reader.readS(); + pat->newData[j][DIV_PAT_FXVAL(k)]=reader.readS(); } } @@ -2171,7 +2169,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) { return true; } -SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) { +SafeWriter* DivEngine::saveFur(bool notPrimary) { saveLock.lock(); std::vector subSongPtr; std::vector sysFlagsPtr; @@ -2622,133 +2620,100 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) { DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false); patPtr.push_back(w->tell()); - if (newPatternFormat) { - w->write("PATN",4); - blockStartSeek=w->tell(); - w->writeI(0); + w->write("PATN",4); + blockStartSeek=w->tell(); + w->writeI(0); - w->writeC(i.subsong); - w->writeC(i.chan); - w->writeS(i.pat); - w->writeString(pat->name,false); + w->writeC(i.subsong); + w->writeC(i.chan); + w->writeS(i.pat); + w->writeString(pat->name,false); - unsigned char emptyRows=0; + unsigned char emptyRows=0; - for (int j=0; jpatLen; j++) { - unsigned char mask=0; - unsigned char finalNote=255; - unsigned short effectMask=0; + for (int j=0; jpatLen; j++) { + unsigned char mask=0; + unsigned char finalNote=255; + unsigned short effectMask=0; - if (pat->data[j][0]==100) { - finalNote=180; - } else if (pat->data[j][0]==101) { // note release - finalNote=181; - } else if (pat->data[j][0]==102) { // macro release - finalNote=182; - } else if (pat->data[j][1]==0 && pat->data[j][0]==0) { - finalNote=255; - } else { - int seek=(pat->data[j][0]+(signed char)pat->data[j][1]*12)+60; - if (seek<0 || seek>=180) { - finalNote=255; - } else { - finalNote=seek; - } - } - - if (finalNote!=255) mask|=1; // note - if (pat->data[j][2]!=-1) mask|=2; // instrument - if (pat->data[j][3]!=-1) mask|=4; // volume - for (int k=0; kpat[i.chan].effectCols*2; k+=2) { - if (k==0) { - if (pat->data[j][4+k]!=-1) mask|=8; - if (pat->data[j][5+k]!=-1) mask|=16; - } else if (k<8) { - if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=32; - } else { - if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=64; - } - - if (pat->data[j][4+k]!=-1) effectMask|=(1<data[j][5+k]!=-1) effectMask|=(2<127) { - w->writeC(128|(emptyRows-2)); - emptyRows=0; - } - } else { - if (emptyRows>1) { - w->writeC(128|(emptyRows-2)); - emptyRows=0; - } else if (emptyRows) { - w->writeC(0); - emptyRows=0; - } - - w->writeC(mask); - - if (mask&32) w->writeC(effectMask&0xff); - if (mask&64) w->writeC((effectMask>>8)&0xff); - - if (mask&1) w->writeC(finalNote); - if (mask&2) w->writeC(pat->data[j][2]); - if (mask&4) w->writeC(pat->data[j][3]); - if (mask&8) w->writeC(pat->data[j][4]); - if (mask&16) w->writeC(pat->data[j][5]); - if (mask&32) { - if (effectMask&4) w->writeC(pat->data[j][6]); - if (effectMask&8) w->writeC(pat->data[j][7]); - if (effectMask&16) w->writeC(pat->data[j][8]); - if (effectMask&32) w->writeC(pat->data[j][9]); - if (effectMask&64) w->writeC(pat->data[j][10]); - if (effectMask&128) w->writeC(pat->data[j][11]); - } - if (mask&64) { - if (effectMask&256) w->writeC(pat->data[j][12]); - if (effectMask&512) w->writeC(pat->data[j][13]); - if (effectMask&1024) w->writeC(pat->data[j][14]); - if (effectMask&2048) w->writeC(pat->data[j][15]); - if (effectMask&4096) w->writeC(pat->data[j][16]); - if (effectMask&8192) w->writeC(pat->data[j][17]); - if (effectMask&16384) w->writeC(pat->data[j][18]); - if (effectMask&32768) w->writeC(pat->data[j][19]); - } - } + if (pat->newData[j][DIV_PAT_NOTE]==DIV_NOTE_OFF) { // note off + finalNote=180; + } else if (pat->newData[j][DIV_PAT_NOTE]==DIV_NOTE_REL) { // note release + finalNote=181; + } else if (pat->newData[j][DIV_PAT_NOTE]==DIV_MACRO_REL) { // macro release + finalNote=182; + } else if (pat->newData[j][DIV_PAT_NOTE]==-1) { // empty + finalNote=255; + } else { + finalNote=pat->newData[j][DIV_PAT_NOTE]; } - // stop - w->writeC(0xff); - } else { - w->write("PATR",4); - blockStartSeek=w->tell(); - w->writeI(0); - - w->writeS(i.chan); - w->writeS(i.pat); - w->writeS(i.subsong); - - w->writeS(0); // reserved - - for (int j=0; jpatLen; j++) { - w->writeS(pat->data[j][0]); // note - w->writeS(pat->data[j][1]); // octave - w->writeS(pat->data[j][2]); // instrument - w->writeS(pat->data[j][3]); // volume -#ifdef TA_BIG_ENDIAN - for (int k=0; kpat[i.chan].effectCols*2; k++) { - w->writeS(pat->data[j][4+k]); + if (finalNote!=255) mask|=1; // note + if (pat->newData[j][DIV_PAT_INS]!=-1) mask|=2; // instrument + if (pat->newData[j][DIV_PAT_VOL]!=-1) mask|=4; // volume + for (int k=0; kpat[i.chan].effectCols*2; k+=2) { + if (k==0) { + if (pat->newData[j][DIV_PAT_FX(0)+k]!=-1) mask|=8; + if (pat->newData[j][DIV_PAT_FXVAL(0)+k]!=-1) mask|=16; + } else if (k<8) { + if (pat->newData[j][DIV_PAT_FX(0)+k]!=-1 || pat->newData[j][DIV_PAT_FXVAL(0)+k]!=-1) mask|=32; + } else { + if (pat->newData[j][DIV_PAT_FX(0)+k]!=-1 || pat->newData[j][DIV_PAT_FXVAL(0)+k]!=-1) mask|=64; } -#else - w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects -#endif + + if (pat->newData[j][DIV_PAT_FX(0)+k]!=-1) effectMask|=(1<newData[j][DIV_PAT_FXVAL(0)+k]!=-1) effectMask|=(2<writeString(pat->name,false); + if (mask==0) { + emptyRows++; + if (emptyRows>127) { + w->writeC(128|(emptyRows-2)); + emptyRows=0; + } + } else { + if (emptyRows>1) { + w->writeC(128|(emptyRows-2)); + emptyRows=0; + } else if (emptyRows) { + w->writeC(0); + emptyRows=0; + } + + w->writeC(mask); + + if (mask&32) w->writeC(effectMask&0xff); + if (mask&64) w->writeC((effectMask>>8)&0xff); + + if (mask&1) w->writeC(finalNote); + if (mask&2) w->writeC(pat->newData[j][DIV_PAT_INS]); + if (mask&4) w->writeC(pat->newData[j][DIV_PAT_VOL]); + if (mask&8) w->writeC(pat->newData[j][DIV_PAT_FX(0)]); + if (mask&16) w->writeC(pat->newData[j][DIV_PAT_FXVAL(0)]); + if (mask&32) { + if (effectMask&4) w->writeC(pat->newData[j][DIV_PAT_FX(1)]); + if (effectMask&8) w->writeC(pat->newData[j][DIV_PAT_FXVAL(1)]); + if (effectMask&16) w->writeC(pat->newData[j][DIV_PAT_FX(2)]); + if (effectMask&32) w->writeC(pat->newData[j][DIV_PAT_FXVAL(2)]); + if (effectMask&64) w->writeC(pat->newData[j][DIV_PAT_FX(3)]); + if (effectMask&128) w->writeC(pat->newData[j][DIV_PAT_FXVAL(3)]); + } + if (mask&64) { + if (effectMask&256) w->writeC(pat->newData[j][DIV_PAT_FX(4)]); + if (effectMask&512) w->writeC(pat->newData[j][DIV_PAT_FXVAL(4)]); + if (effectMask&1024) w->writeC(pat->newData[j][DIV_PAT_FX(5)]); + if (effectMask&2048) w->writeC(pat->newData[j][DIV_PAT_FXVAL(5)]); + if (effectMask&4096) w->writeC(pat->newData[j][DIV_PAT_FX(6)]); + if (effectMask&8192) w->writeC(pat->newData[j][DIV_PAT_FXVAL(6)]); + if (effectMask&16384) w->writeC(pat->newData[j][DIV_PAT_FX(7)]); + if (effectMask&32768) w->writeC(pat->newData[j][DIV_PAT_FXVAL(7)]); + } + } } + // stop + w->writeC(0xff); + blockEndSeek=w->tell(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); diff --git a/src/engine/fileOps/it.cpp b/src/engine/fileOps/it.cpp index e31d78180..12513621b 100644 --- a/src/engine/fileOps/it.cpp +++ b/src/engine/fileOps/it.cpp @@ -1062,7 +1062,7 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { int readRow=0; bool mustCommitInitial=true; - memset(effectCol,4,64); + memset(effectCol,0,64); memset(vibStatus,0,64); memset(vibStatusChanged,0,64*sizeof(bool)); memset(vibing,0,64*sizeof(bool)); @@ -1139,86 +1139,86 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { for (int j=0; j<64; j++) { DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true); if (vibing[j]!=vibingOld[j] || vibStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x04; - p->data[readRow][effectCol[j]++]=vibing[j]?vibStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x04; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=vibing[j]?vibStatus[j]:0; doesVibrato[j]=true; } else if (doesVibrato[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x04; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x04; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (volSliding[j]!=volSlidingOld[j] || volSlideStatusChanged[j]) { if (volSlideStatus[j]>=0xf1 && volSliding[j]) { - p->data[readRow][effectCol[j]++]=0xf9; - p->data[readRow][effectCol[j]++]=volSlideStatus[j]&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xf9; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSlideStatus[j]&15; volSliding[j]=false; } else if ((volSlideStatus[j]&15)==15 && volSlideStatus[j]>=0x10 && volSliding[j]) { - p->data[readRow][effectCol[j]++]=0xf8; - p->data[readRow][effectCol[j]++]=volSlideStatus[j]>>4; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xf8; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSlideStatus[j]>>4; volSliding[j]=false; } else { - p->data[readRow][effectCol[j]++]=0xfa; - p->data[readRow][effectCol[j]++]=volSliding[j]?volSlideStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xfa; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSliding[j]?volSlideStatus[j]:0; } doesVolSlide[j]=true; } else if (doesVolSlide[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0xfa; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xfa; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (porting[j]!=portingOld[j] || portaStatusChanged[j]) { if (portaStatus[j]>=0xe0 && portaType[j]!=3 && porting[j]) { - p->data[readRow][effectCol[j]++]=portaType[j]|0xf0; - p->data[readRow][effectCol[j]++]=(portaStatus[j]&15)*((portaStatus[j]>=0xf0)?1:1); + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=portaType[j]|0xf0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=(portaStatus[j]&15)*((portaStatus[j]>=0xf0)?1:1); porting[j]=false; } else { - p->data[readRow][effectCol[j]++]=portaType[j]; - p->data[readRow][effectCol[j]++]=porting[j]?portaStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=portaType[j]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=porting[j]?portaStatus[j]:0; } doesPitchSlide[j]=true; } else if (doesPitchSlide[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x01; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x01; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (arping[j]!=arpingOld[j] || arpStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x00; - p->data[readRow][effectCol[j]++]=arping[j]?arpStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x00; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=arping[j]?arpStatus[j]:0; doesArp[j]=true; } else if (doesArp[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x00; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x00; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (treming[j]!=tremingOld[j] || tremStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x07; - p->data[readRow][effectCol[j]++]=treming[j]?tremStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x07; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=treming[j]?tremStatus[j]:0; doesTremolo[j]=true; } else if (doesTremolo[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x07; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x07; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (panning[j]!=panningOld[j] || panStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x84; - p->data[readRow][effectCol[j]++]=panning[j]?panStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x84; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=panning[j]?panStatus[j]:0; doesPanbrello[j]=true; } else if (doesPanbrello[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x84; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x84; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (panSliding[j]!=panSlidingOld[j] || panSlideStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x83; - p->data[readRow][effectCol[j]++]=panSliding[j]?panSlideStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x83; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=panSliding[j]?panSlideStatus[j]:0; doesPanSlide[j]=true; } else if (doesPanSlide[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x83; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x83; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } - if ((effectCol[j]>>1)-2>ds.subsong[0]->pat[j].effectCols) { - ds.subsong[0]->pat[j].effectCols=(effectCol[j]>>1)-1; + if ((effectCol[j]>>1)>=ds.subsong[0]->pat[j].effectCols) { + ds.subsong[0]->pat[j].effectCols=(effectCol[j]>>1)+1; } } @@ -1250,16 +1250,16 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { if (readRow>0) { // place end of pattern marker DivPattern* p=ds.subsong[0]->pat[0].getPattern(i,true); - p->data[readRow-1][effectCol[0]++]=0x0d; - p->data[readRow-1][effectCol[0]++]=0; + p->newData[readRow-1][DIV_PAT_FX(0)+effectCol[0]++]=0x0d; + p->newData[readRow-1][DIV_PAT_FX(0)+effectCol[0]++]=0; - if ((effectCol[0]>>1)-2>ds.subsong[0]->pat[0].effectCols) { - ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)-1; + if ((effectCol[0]>>1)>=ds.subsong[0]->pat[0].effectCols) { + ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)+1; } } break; } - memset(effectCol,4,64); + memset(effectCol,0,64); continue; } @@ -1302,25 +1302,17 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { if (hasNote) { if (note[chan]==255) { // note release - p->data[readRow][0]=101; - p->data[readRow][1]=0; + p->newData[readRow][DIV_PAT_NOTE]=DIV_NOTE_REL; } else if (note[chan]==254) { // note off - p->data[readRow][0]=100; - p->data[readRow][1]=0; + p->newData[readRow][DIV_PAT_NOTE]=DIV_NOTE_OFF; } else if (note[chan]<120) { - p->data[readRow][0]=note[chan]%12; - p->data[readRow][1]=note[chan]/12; - if (p->data[readRow][0]==0) { - p->data[readRow][0]=12; - p->data[readRow][1]--; - } + p->newData[readRow][DIV_PAT_NOTE]=note[chan]+60; } else { // note fade, but Furnace does not support that - p->data[readRow][0]=102; - p->data[readRow][1]=0; + p->newData[readRow][DIV_PAT_NOTE]=DIV_MACRO_REL; } } if (hasIns) { - p->data[readRow][2]=ins[chan]-1; + p->newData[readRow][DIV_PAT_INS]=ins[chan]-1; if ((note[chan]<120 || ds.insLen==0) && ins[chan]>0) { unsigned char targetPan=0; if (ds.insLen==0) { @@ -1332,26 +1324,26 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { } } if (targetPan&128) { - p->data[readRow][effectCol[chan]++]=0x80; - p->data[readRow][effectCol[chan]++]=CLAMP((targetPan&127)<<2,0,255); + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=CLAMP((targetPan&127)<<2,0,255); } } if (hasNote && (note[chan]<120 || ds.insLen==0) && ins[chan]>0) { if (ds.insLen==0) { - p->data[readRow][3]=defVol[(ins[chan]-1)&255]; + p->newData[readRow][DIV_PAT_VOL]=defVol[(ins[chan]-1)&255]; } else { - p->data[readRow][3]=defVol[noteMap[(ins[chan]-1)&255][note[chan]]]; + p->newData[readRow][DIV_PAT_VOL]=defVol[noteMap[(ins[chan]-1)&255][note[chan]]]; } } } if (hasVol) { if (vol[chan]<=64) { - p->data[readRow][3]=vol[chan]; + p->newData[readRow][DIV_PAT_VOL]=vol[chan]; } else { // effects in volume column if (vol[chan]>=128 && vol[chan]<=192) { // panning - p->data[readRow][effectCol[chan]++]=0x80; - p->data[readRow][effectCol[chan]++]=CLAMP((vol[chan]-128)<<2,0,255); + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=CLAMP((vol[chan]-128)<<2,0,255); } else if (vol[chan]>=65 && vol[chan]<=74) { // fine vol up } else if (vol[chan]>=75 && vol[chan]<=84) { // fine vol down } else if (vol[chan]>=85 && vol[chan]<=94) { // vol slide up @@ -1398,16 +1390,16 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { if (hasEffect) { switch (effect[chan]+'A'-1) { case 'A': // speed - p->data[readRow][effectCol[chan]++]=0x0f; - p->data[readRow][effectCol[chan]++]=effectVal[chan]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0f; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]; break; case 'B': // go to order - p->data[readRow][effectCol[chan]++]=0x0b; - p->data[readRow][effectCol[chan]++]=orders[effectVal[chan]]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0b; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=orders[effectVal[chan]]; break; case 'C': // next order - p->data[readRow][effectCol[chan]++]=0x0d; - p->data[readRow][effectCol[chan]++]=effectVal[chan]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0d; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]; break; case 'D': // vol slide if (effectVal[chan]!=0) { @@ -1490,8 +1482,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { case 'N': // channel vol slide break; case 'O': // offset - p->data[readRow][effectCol[chan]++]=0x91; - p->data[readRow][effectCol[chan]++]=effectVal[chan]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x91; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]; break; case 'P': // pan slide if (effectVal[chan]!=0) { @@ -1504,8 +1496,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { if (effectVal[chan]!=0) { lastRetrig[chan]=effectVal[chan]; } - p->data[readRow][effectCol[chan]++]=0x0c; - p->data[readRow][effectCol[chan]++]=lastRetrig[chan]&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0c; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=lastRetrig[chan]&15; break; case 'R': // tremolo if (effectVal[chan]!=0) { @@ -1519,77 +1511,77 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { case 0x3: // vibrato waveform switch (effectVal[chan]&3) { case 0x0: // sine - p->data[readRow][effectCol[chan]++]=0xe3; - p->data[readRow][effectCol[chan]++]=0x00; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x00; break; case 0x1: // ramp down - p->data[readRow][effectCol[chan]++]=0xe3; - p->data[readRow][effectCol[chan]++]=0x05; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x05; break; case 0x2: // square - p->data[readRow][effectCol[chan]++]=0xe3; - p->data[readRow][effectCol[chan]++]=0x06; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x06; break; case 0x3: // random - p->data[readRow][effectCol[chan]++]=0xe3; - p->data[readRow][effectCol[chan]++]=0x07; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x07; break; } break; case 0x7: switch (effectVal[chan]&15) { case 0x7: // volume envelope off - p->data[readRow][effectCol[chan]++]=0xf5; - p->data[readRow][effectCol[chan]++]=0x00; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf5; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x00; break; case 0x8: // volume envelope on - p->data[readRow][effectCol[chan]++]=0xf6; - p->data[readRow][effectCol[chan]++]=0x00; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf6; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x00; break; case 0x9: // panning envelope off - p->data[readRow][effectCol[chan]++]=0xf5; - p->data[readRow][effectCol[chan]++]=0x0c; - p->data[readRow][effectCol[chan]++]=0xf5; - p->data[readRow][effectCol[chan]++]=0x0d; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf5; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0c; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf5; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0d; break; case 0xa: // panning envelope on - p->data[readRow][effectCol[chan]++]=0xf6; - p->data[readRow][effectCol[chan]++]=0x0c; - p->data[readRow][effectCol[chan]++]=0xf6; - p->data[readRow][effectCol[chan]++]=0x0d; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf6; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0c; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf6; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0d; break; case 0xb: // pitch envelope off - p->data[readRow][effectCol[chan]++]=0xf5; - p->data[readRow][effectCol[chan]++]=0x04; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf5; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x04; break; case 0xc: //pitch envelope on - p->data[readRow][effectCol[chan]++]=0xf6; - p->data[readRow][effectCol[chan]++]=0x04; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf6; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x04; break; } break; case 0x8: // panning - p->data[readRow][effectCol[chan]++]=0x80; - p->data[readRow][effectCol[chan]++]=(effectVal[chan]&15)<<4; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=(effectVal[chan]&15)<<4; break; case 0xa: // offset (high nibble) - p->data[readRow][effectCol[chan]++]=0x92; - p->data[readRow][effectCol[chan]++]=effectVal[chan]&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x92; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]&15; break; case 0xc: // note cut - p->data[readRow][effectCol[chan]++]=0xec; - p->data[readRow][effectCol[chan]++]=effectVal[chan]&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xec; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]&15; break; case 0xd: // note delay - p->data[readRow][effectCol[chan]++]=0xed; - p->data[readRow][effectCol[chan]++]=effectVal[chan]&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xed; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]&15; break; } break; case 'T': // tempo if (effectVal[chan]>=0x20) { - p->data[readRow][effectCol[chan]++]=0xf0; - p->data[readRow][effectCol[chan]++]=effectVal[chan]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]; } break; case 'U': // fine vibrato @@ -1604,8 +1596,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { case 'W': // global volume slide (!) break; case 'X': // panning - p->data[readRow][effectCol[chan]++]=0x80; - p->data[readRow][effectCol[chan]++]=effectVal[chan]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal[chan]; break; case 'Y': // panbrello if (effectVal[chan]!=0) { @@ -1690,18 +1682,18 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { for (int j=0; jpat[j].getPattern(ds.subsong[i]->orders.ord[j][0],true); for (int k=0; kdata[0][4+(k<<1)]==0x80) { + if (p->newData[0][DIV_PAT_FX(k)]==0x80) { // give up if there's a panning effect already break; } - if (p->data[0][4+(k<<1)]==-1) { + if (p->newData[0][DIV_PAT_FX(k)]==-1) { if ((chanPan[j]&127)==100) { // should be surround... - p->data[0][4+(k<<1)]=0x80; - p->data[0][5+(k<<1)]=0x80; + p->newData[0][DIV_PAT_FX(k)]=0x80; + p->newData[0][DIV_PAT_FXVAL(k)]=0x80; } else { - p->data[0][4+(k<<1)]=0x80; - p->data[0][5+(k<<1)]=CLAMP((chanPan[j]&127)<<2,0,255); + p->newData[0][DIV_PAT_FX(k)]=0x80; + p->newData[0][DIV_PAT_FXVAL(k)]=CLAMP((chanPan[j]&127)<<2,0,255); } if (ds.subsong[i]->pat[j].effectCols<=k) ds.subsong[i]->pat[j].effectCols=k+1; break; diff --git a/src/engine/fileOps/mod.cpp b/src/engine/fileOps/mod.cpp index 8cdd4a61f..6672a0242 100644 --- a/src/engine/fileOps/mod.cpp +++ b/src/engine/fileOps/mod.cpp @@ -185,21 +185,20 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { } for (int row=0; row<64; row++) { for (int ch=0; chdata[row]; + short* dstrowN=chpats[ch]->newData[row]; unsigned char data[4]; reader.read(&data,4); // instrument short ins=(data[0]&0xf0)|(data[2]>>4); if (ins>0) { - dstrow[2]=ins-1; - dstrow[3]=defaultVols[ins-1]; + dstrowN[DIV_PAT_INS]=ins-1; + dstrowN[DIV_PAT_VOL]=defaultVols[ins-1]; } // note int period=data[1]+((data[0]&0x0f)<<8); if (period>0 && period<0x0fff) { short note=(short)round(log2(3424.0/period)*12); - dstrow[0]=((note+11)%12)+1; - dstrow[1]=(note-1)/12+1; + dstrowN[DIV_PAT_NOTE]=note+72; if (period<114) { bypassLimits=true; } @@ -207,8 +206,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { // effects are done later short fxtyp=data[2]&0x0f; short fxval=data[3]; - dstrow[4]=fxtyp; - dstrow[5]=fxval; + dstrowN[DIV_PAT_FX(0)]=fxtyp; + dstrowN[DIV_PAT_FXVAL(0)]=fxval; switch (fxtyp) { case 0: if (fxval!=0) fxUsage[ch][0]=true; @@ -256,7 +255,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { for (int ch=0; ch<=chCount; ch++) { unsigned char fxCols=1; for (int pat=0; pat<=patMax; pat++) { - auto* data=ds.subsong[0]->pat[ch].getPattern(pat,true)->data; + auto* newData=ds.subsong[0]->pat[ch].getPattern(pat,true)->newData; short lastPitchEffect=-1; short lastEffectState[5]={-1,-1,-1,-1,-1}; short setEffectState[5]={-1,-1,-1,-1,-1}; @@ -264,11 +263,11 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { const short fxUsageTyp[5]={0x00,0x01,0x04,0x07,0xFA}; short effectState[5]={0,0,0,0,0}; unsigned char curFxCol=0; - short fxTyp=data[row][4]; - short fxVal=data[row][5]; - auto writeFxCol=[data,row,&curFxCol](short typ, short val) { - data[row][4+curFxCol*2]=typ; - data[row][5+curFxCol*2]=val; + short fxTyp=newData[row][DIV_PAT_FX(0)]; + short fxVal=newData[row][DIV_PAT_FXVAL(0)]; + auto writeFxCol=[newData,row,&curFxCol](short typ, short val) { + newData[row][DIV_PAT_FX(curFxCol)]=typ; + newData[row][DIV_PAT_FXVAL(curFxCol)]=val; curFxCol++; }; writeFxCol(-1,-1); @@ -293,7 +292,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { effectState[1]=fxVal; if ((effectState[1]!=lastEffectState[1]) || (fxTyp!=lastPitchEffect) || - (effectState[1]!=0 && data[row][0]>0)) { + (effectState[1]!=0 && newData[row][DIV_PAT_NOTE]>-1)) { writeFxCol(fxTyp,fxVal); } lastPitchEffect=fxTyp; @@ -331,7 +330,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { writeFxCol(fxTyp,fxVal); break; case 12: // set vol - data[row][3]=MIN(0x40,fxVal); + newData[row][DIV_PAT_VOL]=MIN(0x40,fxVal); break; case 13: // break to row (BCD) writeFxCol(fxTyp,((fxVal>>4)*10)+(fxVal&15)); @@ -387,7 +386,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { for (int i=0; i<5; i++) { // pitch slide and volume slide needs to be kept active on new note // even after target/max is reached - if (fxUsage[ch][i] && (effectState[i]!=lastEffectState[i] || (effectState[i]!=0 && i==4 && data[row][3]>=0))) { + if (fxUsage[ch][i] && (effectState[i]!=lastEffectState[i] || (effectState[i]!=0 && i==4 && newData[row][DIV_PAT_VOL]>=0))) { writeFxCol(fxUsageTyp[i],effectState[i]); } } diff --git a/src/engine/fileOps/s3m.cpp b/src/engine/fileOps/s3m.cpp index 2aaf6f757..1000273fb 100644 --- a/src/engine/fileOps/s3m.cpp +++ b/src/engine/fileOps/s3m.cpp @@ -771,7 +771,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { bool mustCommitInitial=true; - memset(effectCol,4,32); + memset(effectCol,0,32); memset(vibStatus,0,32); memset(vibStatusChanged,0,32*sizeof(bool)); memset(vibing,0,32*sizeof(bool)); @@ -811,95 +811,95 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { for (int j=0; j<32; j++) { DivPattern* p=ds.subsong[0]->pat[chanMap[j]].getPattern(i,true); if (vibing[j]!=vibingOld[j] || vibStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x04; - p->data[readRow][effectCol[j]++]=vibing[j]?vibStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x04; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=vibing[j]?vibStatus[j]:0; doesVibrato[j]=true; } else if (doesVibrato[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x04; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x04; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (volSliding[j]!=volSlidingOld[j] || volSlideStatusChanged[j]) { if (volSlideStatus[j]>=0xf1 && volSliding[j]) { - p->data[readRow][effectCol[j]++]=0xf9; - p->data[readRow][effectCol[j]++]=volSlideStatus[j]&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xf9; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSlideStatus[j]&15; volSliding[j]=false; } else if ((volSlideStatus[j]&15)==15 && volSlideStatus[j]>=0x10 && volSliding[j]) { - p->data[readRow][effectCol[j]++]=0xf8; - p->data[readRow][effectCol[j]++]=volSlideStatus[j]>>4; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xf8; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSlideStatus[j]>>4; volSliding[j]=false; } else { - p->data[readRow][effectCol[j]++]=0xfa; - p->data[readRow][effectCol[j]++]=volSliding[j]?volSlideStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xfa; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=volSliding[j]?volSlideStatus[j]:0; } doesVolSlide[j]=true; } else if (doesVolSlide[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0xfa; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0xfa; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (porting[j]!=portingOld[j] || portaStatusChanged[j]) { if (portaStatus[j]>=0xe0 && portaType[j]!=3 && porting[j]) { - p->data[readRow][effectCol[j]++]=portaType[j]|0xf0; - p->data[readRow][effectCol[j]++]=(portaStatus[j]&15)*((portaStatus[j]>=0xf0)?1:1); + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=portaType[j]|0xf0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=(portaStatus[j]&15)*((portaStatus[j]>=0xf0)?1:1); porting[j]=false; } else { - p->data[readRow][effectCol[j]++]=portaType[j]; - p->data[readRow][effectCol[j]++]=porting[j]?portaStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=portaType[j]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=porting[j]?portaStatus[j]:0; } doesPitchSlide[j]=true; } else if (doesPitchSlide[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x01; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x01; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (arping[j]!=arpingOld[j] || arpStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x00; - p->data[readRow][effectCol[j]++]=arping[j]?arpStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x00; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=arping[j]?arpStatus[j]:0; doesArp[j]=true; } else if (doesArp[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x00; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x00; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (treming[j]!=tremingOld[j] || tremStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x07; - p->data[readRow][effectCol[j]++]=treming[j]?tremStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x07; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=treming[j]?tremStatus[j]:0; doesTremolo[j]=true; } else if (doesTremolo[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x07; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x07; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (panning[j]!=panningOld[j] || panStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x84; - p->data[readRow][effectCol[j]++]=panning[j]?panStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x84; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=panning[j]?panStatus[j]:0; doesPanbrello[j]=true; } else if (doesPanbrello[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x84; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x84; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } if (panSliding[j]!=panSlidingOld[j] || panSlideStatusChanged[j]) { - p->data[readRow][effectCol[j]++]=0x83; - p->data[readRow][effectCol[j]++]=panSliding[j]?panSlideStatus[j]:0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x83; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=panSliding[j]?panSlideStatus[j]:0; doesPanSlide[j]=true; } else if (doesPanSlide[j] && mustCommitInitial) { - p->data[readRow][effectCol[j]++]=0x83; - p->data[readRow][effectCol[j]++]=0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0x83; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[j]++]=0; } - if (effectCol[j]>=4+8*2) { + if (effectCol[j]>=8*2) { logE("oh crap!"); } - if ((effectCol[j]>>1)-2>ds.subsong[0]->pat[j].effectCols) { - ds.subsong[0]->pat[chanMap[j]].effectCols=(effectCol[j]>>1)-1; + if ((effectCol[j]>>1)>=ds.subsong[0]->pat[j].effectCols) { + ds.subsong[0]->pat[chanMap[j]].effectCols=(effectCol[j]>>1)+1; } } readRow++; - memset(effectCol,4,32); + memset(effectCol,0,32); memcpy(vibingOld,vibing,32*sizeof(bool)); memcpy(volSlidingOld,volSliding,32*sizeof(bool)); memcpy(portingOld,porting,32*sizeof(bool)); @@ -935,22 +935,16 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { unsigned char ins=reader.readC(); if (note==254) { // note off - p->data[readRow][0]=100; - p->data[readRow][1]=0; + p->newData[readRow][DIV_PAT_NOTE]=DIV_NOTE_OFF; } else if (note!=255) { - p->data[readRow][0]=note&15; - p->data[readRow][1]=note>>4; - if ((note&15)==0) { - p->data[readRow][0]=12; - p->data[readRow][1]--; - } + p->newData[readRow][DIV_PAT_NOTE]=(note&15)+(note>>4)*12+60; } - p->data[readRow][2]=(short)ins-1; + p->newData[readRow][DIV_PAT_INS]=(short)ins-1; } if (hasVol) { unsigned char vol=reader.readC(); if (vol==255) { - p->data[readRow][3]=-1; + p->newData[readRow][DIV_PAT_VOL]=-1; } else { // check for OPL channel if ((chanSettings[chan]&31)>=16) { @@ -958,17 +952,17 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { } else { if (vol>64) vol=64; } - p->data[readRow][3]=vol; + p->newData[readRow][DIV_PAT_VOL]=vol; } - } else if (p->data[readRow][2]!=-1) { + } else if (p->newData[readRow][DIV_PAT_INS]!=-1) { // populate with instrument volume - unsigned char vol=defVol[p->data[readRow][2]&255]; + unsigned char vol=defVol[p->newData[readRow][DIV_PAT_INS]&255]; if ((chanSettings[chan]&31)>=16) { if (vol>63) vol=63; } else { if (vol>64) vol=64; } - p->data[readRow][3]=vol; + p->newData[readRow][DIV_PAT_VOL]=vol; } if (hasEffect) { unsigned char effect=reader.readC(); @@ -976,17 +970,17 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { switch (effect+'A'-1) { case 'A': // speed - p->data[readRow][effectCol[chan]++]=0x0f; - p->data[readRow][effectCol[chan]++]=effectVal; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0f; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal; break; case 'B': // go to order - p->data[readRow][effectCol[chan]++]=0x0b; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0b; logD("0B: %x %x",effectVal,orders[effectVal]); - p->data[readRow][effectCol[chan]++]=orders[effectVal]; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=orders[effectVal]; break; case 'C': // next order - p->data[readRow][effectCol[chan]++]=0x0d; - p->data[readRow][effectCol[chan]++]=(effectVal>>4)*10+(effectVal&15); + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0d; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=(effectVal>>4)*10+(effectVal&15); break; case 'D': // vol slide if (effectVal!=0) { @@ -1078,8 +1072,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { case 'N': // channel vol slide (extension) break; case 'O': // offset - p->data[readRow][effectCol[chan]++]=0x91; - p->data[readRow][effectCol[chan]++]=effectVal; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x91; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal; break; case 'P': // pan slide (extension) if (effectVal!=0) { @@ -1089,8 +1083,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { panSliding[chan]=true; break; case 'Q': // retrigger - p->data[readRow][effectCol[chan]++]=0x0c; - p->data[readRow][effectCol[chan]++]=effectVal&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x0c; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal&15; break; case 'R': // tremolo if (effectVal!=0) { @@ -1104,40 +1098,40 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { case 0x3: // vibrato waveform switch (effectVal&3) { case 0x0: // sine - p->data[readRow][effectCol[chan]++]=0xe3; - p->data[readRow][effectCol[chan]++]=0x00; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x00; break; case 0x1: // ramp down - p->data[readRow][effectCol[chan]++]=0xe3; - p->data[readRow][effectCol[chan]++]=0x05; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x05; break; case 0x2: // square - p->data[readRow][effectCol[chan]++]=0xe3; - p->data[readRow][effectCol[chan]++]=0x06; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x06; break; case 0x3: // random - p->data[readRow][effectCol[chan]++]=0xe3; - p->data[readRow][effectCol[chan]++]=0x07; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xe3; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x07; break; } break; case 0x8: // panning - p->data[readRow][effectCol[chan]++]=0x80; - p->data[readRow][effectCol[chan]++]=(effectVal&15)<<4; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=(effectVal&15)<<4; break; case 0xc: // note cut - p->data[readRow][effectCol[chan]++]=0xec; - p->data[readRow][effectCol[chan]++]=effectVal&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xec; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal&15; break; case 0xd: // note delay - p->data[readRow][effectCol[chan]++]=0xed; - p->data[readRow][effectCol[chan]++]=effectVal&15; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xed; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal&15; break; } break; case 'T': // tempo - p->data[readRow][effectCol[chan]++]=0xf0; - p->data[readRow][effectCol[chan]++]=effectVal; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0xf0; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=effectVal; break; case 'U': // fine vibrato if (effectVal!=0) { @@ -1152,8 +1146,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { break; case 'X': // panning (extension) if (effectVal<=0x80) { - p->data[readRow][effectCol[chan]++]=0x80; - p->data[readRow][effectCol[chan]++]=(effectVal&0x80)?0xff:(effectVal<<1); + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=0x80; + p->newData[readRow][DIV_PAT_FX(0)+effectCol[chan]++]=(effectVal&0x80)?0xff:(effectVal<<1); } break; case 'Y': // panbrello (extension) @@ -1193,16 +1187,16 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { for (int j=0; j<16; j++) { DivPattern* p=ds.subsong[i]->pat[chanMap[j]].getPattern(ds.subsong[i]->orders.ord[j][0],true); for (int k=0; kdata[0][4+(k<<1)]==0x80) { + if (p->newData[0][DIV_PAT_FX(k)]==0x80) { // give up if there's a panning effect already break; } - if (p->data[0][4+(k<<1)]==-1) { - p->data[0][4+(k<<1)]=0x80; + if (p->newData[0][DIV_PAT_FX(k)]==-1) { + p->newData[0][DIV_PAT_FX(k)]=0x80; if (chanPan[j]&16) { - p->data[0][5+(k<<1)]=(j&1)?0xcc:0x33; + p->newData[0][DIV_PAT_FXVAL(k)]=(j&1)?0xcc:0x33; } else { - p->data[0][5+(k<<1)]=(chanPan[j]&15)|((chanPan[j]&15)<<4); + p->newData[0][DIV_PAT_FXVAL(k)]=(chanPan[j]&15)|((chanPan[j]&15)<<4); } if (ds.subsong[i]->pat[chanMap[j]].effectCols<=k) ds.subsong[i]->pat[chanMap[j]].effectCols=k+1; break; diff --git a/src/engine/fileOps/text.cpp b/src/engine/fileOps/text.cpp index f4b1105ec..83b4b9ae6 100644 --- a/src/engine/fileOps/text.cpp +++ b/src/engine/fileOps/text.cpp @@ -321,9 +321,8 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) { for (int l=0; lpat[l].getPattern(s->orders.ord[l][j],false); - - int note=p->data[k][0]; - int octave=p->data[k][1]; + short note, octave; + noteToSplitNote(p->newData[k][DIV_PAT_NOTE],note,octave); if (note==0 && octave==0) { w->writeText("|... "); @@ -344,28 +343,28 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) { w->writeText(fmt::sprintf("|%s%d ",(octave<0)?notesNegative[note]:notes[note],(octave<0)?(-octave):octave)); } - if (p->data[k][2]==-1) { + if (p->newData[k][DIV_PAT_INS]==-1) { w->writeText(".. "); } else { - w->writeText(fmt::sprintf("%.2X ",p->data[k][2]&0xff)); + w->writeText(fmt::sprintf("%.2X ",p->newData[k][DIV_PAT_INS]&0xff)); } - if (p->data[k][3]==-1) { + if (p->newData[k][DIV_PAT_VOL]==-1) { w->writeText(".."); } else { - w->writeText(fmt::sprintf("%.2X",p->data[k][3]&0xff)); + w->writeText(fmt::sprintf("%.2X",p->newData[k][DIV_PAT_VOL]&0xff)); } for (int m=0; mpat[l].effectCols; m++) { - if (p->data[k][4+(m<<1)]==-1) { + if (p->newData[k][DIV_PAT_FX(m)]==-1) { w->writeText(" .."); } else { - w->writeText(fmt::sprintf(" %.2X",p->data[k][4+(m<<1)]&0xff)); + w->writeText(fmt::sprintf(" %.2X",p->newData[k][DIV_PAT_FX(m)]&0xff)); } - if (p->data[k][5+(m<<1)]==-1) { + if (p->newData[k][DIV_PAT_FXVAL(m)]==-1) { w->writeText(".."); } else { - w->writeText(fmt::sprintf("%.2X",p->data[k][5+(m<<1)]&0xff)); + w->writeText(fmt::sprintf("%.2X",p->newData[k][DIV_PAT_FXVAL(m)]&0xff)); } } } diff --git a/src/engine/fileOps/tfm.cpp b/src/engine/fileOps/tfm.cpp index fc3665dd2..015132de2 100644 --- a/src/engine/fileOps/tfm.cpp +++ b/src/engine/fileOps/tfm.cpp @@ -254,16 +254,10 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { if (patDataBuf[k]==0) continue; else if (patDataBuf[k]==1) { // note off - pat->data[k][0]=100; + pat->newData[k][DIV_PAT_NOTE]=DIV_NOTE_OFF; } else { unsigned char invertedNote=~patDataBuf[k]; - pat->data[k][0]=invertedNote%12; - pat->data[k][1]=(invertedNote/12)-1; - - if (pat->data[k][0]==0) { - pat->data[k][0]=12; - pat->data[k][1]--; - } + pat->newData[k][DIV_PAT_NOTE]=invertedNote+60; } } @@ -273,7 +267,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { logD("parsing volumes of pattern %d channel %d",i,j); for (int k=0; k<256; k++) { if (patDataBuf[k]==0) continue; - else pat->data[k][3]=0x60+patDataBuf[k]; + else pat->newData[k][DIV_PAT_VOL]=0x60+patDataBuf[k]; } // instrument @@ -282,7 +276,7 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { logD("parsing instruments of pattern %d channel %d",i,j); for (int k=0; k<256; k++) { if (patDataBuf[k]==0) continue; - pat->data[k][2]=info.insNumMaps[patDataBuf[k]-1]; + pat->newData[k][DIV_PAT_INS]=info.insNumMaps[patDataBuf[k]-1]; } // effects @@ -300,76 +294,76 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { case 0: // arpeggio or no effect (if effect val is 0) if (effectVal[k]==0) break; - pat->data[k][4+(l*2)]=effectNum[k]; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FX(l)]=effectNum[k]; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; break; case 1: // pitch slide up case 2: // pitch slide down - pat->data[k][4+(l*2)]=effectNum[k]; + pat->newData[k][DIV_PAT_FX(l)]=effectNum[k]; if (effectVal[k]) { lastSlide=effectVal[k]; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; } else { - pat->data[k][5+(l*2)]=lastSlide; + pat->newData[k][DIV_PAT_FXVAL(l)]=lastSlide; } break; case 3: // portamento case 4: // vibrato - pat->data[k][5+(l*2)]=0; + pat->newData[k][DIV_PAT_FXVAL(l)]=0; if (effectVal[k]&0xF0) { - pat->data[k][5+(l*2)]|=effectVal[k]&0xF0; + pat->newData[k][DIV_PAT_FXVAL(l)]|=effectVal[k]&0xF0; } else { - pat->data[k][5+(l*2)]|=lastVibrato&0xF0; + pat->newData[k][DIV_PAT_FXVAL(l)]|=lastVibrato&0xF0; } if (effectVal[k]&0x0F) { - pat->data[k][5+(l*2)]|=effectVal[k]&0x0F; + pat->newData[k][DIV_PAT_FXVAL(l)]|=effectVal[k]&0x0F; } else { - pat->data[k][5+(l*2)]|=lastVibrato&0x0F; + pat->newData[k][DIV_PAT_FXVAL(l)]|=lastVibrato&0x0F; } - pat->data[k][4+(l*2)]=effectNum[k]; - lastVibrato=pat->data[k][5+(l*2)]; + pat->newData[k][DIV_PAT_FX(l)]=effectNum[k]; + lastVibrato=pat->newData[k][DIV_PAT_FXVAL(l)]; break; case 5: // poramento + volume slide - pat->data[k][4+(l*2)]=0x06; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FX(l)]=0x06; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; break; case 6: // vibrato + volume slide - pat->data[k][4+(l*2)]=0x05; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FX(l)]=0x05; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; break; case 8: // modify TL of operator 1 - pat->data[k][4+(l*2)]=0x12; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FX(l)]=0x12; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; break; case 9: // modify TL of operator 2 - pat->data[k][4+(l*2)]=0x13; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FX(l)]=0x13; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; break; case 10: // volume slide - pat->data[k][4+(l*2)]=0xA; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FX(l)]=0xA; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; break; case 11: // multi-frequency mode of CH3 control // TODO case 12: // modify TL of operator 3 - pat->data[k][4+(l*2)]=0x14; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FX(l)]=0x14; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; break; case 13: // modify TL of operator 4 - pat->data[k][4+(l*2)]=0x15; - pat->data[k][5+(l*2)]=effectVal[k]; + pat->newData[k][DIV_PAT_FX(l)]=0x15; + pat->newData[k][DIV_PAT_FXVAL(l)]=effectVal[k]; break; case 14: switch (effectVal[k]>>4) { @@ -378,18 +372,18 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { case 2: case 3: // modify multiplier of operators - pat->data[k][4+(l*2)]=0x16; - pat->data[k][5+(l*2)]=((effectVal[k]&0xF0)+0x100)|(effectVal[k]&0xF); + pat->newData[k][DIV_PAT_FX(l)]=0x16; + pat->newData[k][DIV_PAT_FXVAL(l)]=((effectVal[k]&0xF0)+0x100)|(effectVal[k]&0xF); break; case 8: // pan - pat->data[k][4+(l*2)]=0x80; + pat->newData[k][DIV_PAT_FX(l)]=0x80; if ((effectVal[k]&0xF)==1) { - pat->data[k][5+(l*2)]=0; + pat->newData[k][DIV_PAT_FXVAL(l)]=0; } else if ((effectVal[k]&0xF)==2) { - pat->data[k][5+(l*2)]=0xFF; + pat->newData[k][DIV_PAT_FXVAL(l)]=0xFF; } else { - pat->data[k][5+(l*2)]=0x80; + pat->newData[k][DIV_PAT_FXVAL(l)]=0x80; } break; } @@ -407,9 +401,9 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { speed.interleaveFactor=effectVal[k]&0xF; } else if ((effectVal[k]>>4)==(effectVal[k]&0xF)) { // if both speeds are equal - pat->data[k][4+(l*2)]=0x0F; + pat->newData[k][DIV_PAT_FX(l)]=0x0F; unsigned char speedSet=effectVal[k]>>4; - pat->data[k][5+(l*2)]=speedSet; + pat->newData[k][DIV_PAT_FXVAL(l)]=speedSet; break; } else { speed.speedEven=effectVal[k]>>4; @@ -418,8 +412,8 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { auto speedIndex = speeds.find(speed); if (speedIndex != speeds.end()) { - pat->data[k][4+(l*2)]=0x09; - pat->data[k][5+(l*2)]=speedIndex->second; + pat->newData[k][DIV_PAT_FX(l)]=0x09; + pat->newData[k][DIV_PAT_FXVAL(l)]=speedIndex->second; break; } if (speed.interleaveFactor>8) { @@ -437,8 +431,8 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { info.ds->grooves.push_back(groove); speeds[speed]=speedGrooveIndex; - pat->data[k][4+(l*2)]=0x09; - pat->data[k][5+(l*2)]=speedGrooveIndex; + pat->newData[k][DIV_PAT_FX(l)]=0x09; + pat->newData[k][DIV_PAT_FXVAL(l)]=speedGrooveIndex; speedGrooveIndex++; break; } @@ -447,8 +441,8 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { // put a "jump to next pattern" effect if the pattern is smaller than the maximum pattern length if (info.patLens[i]!=0 && info.patLens[i]subsong[0]->patLen) { - pat->data[info.patLens[i]-1][4+(usedEffectsCol*4)]=0x0D; - pat->data[info.patLens[i]-1][5+(usedEffectsCol*4)]=0x00; + pat->newData[info.patLens[i]-1][DIV_PAT_FX(0)+(usedEffectsCol*4)]=0x0D; + pat->newData[info.patLens[i]-1][DIV_PAT_FXVAL(0)+(usedEffectsCol*4)]=0x00; } } } @@ -473,28 +467,30 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { unsigned char truePatLen=(info.patLens[info.orderList[i]]subsong[0]->patLen) ? info.patLens[info.orderList[i]] : info.ds->subsong[0]->patLen; // default instrument - if (i==0 && pat->data[0][2]==-1) pat->data[0][2]=0; + if (i==0 && pat->newData[0][DIV_PAT_INS]==-1) pat->newData[0][DIV_PAT_INS]=0; for (int k=0; kdata[k][4+(l*2)]!=0x00 && pat->data[k][0]!=-1) { - pat->data[k][4+usedEffectsCol*2+(l*2)]=0x00; - pat->data[k][5+usedEffectsCol*2+(l*2)]=0; + // TODO: -1 check? does it still work after refactor? + if (chArpeggio[j] && pat->newData[k][DIV_PAT_FX(l)]!=0x00 && pat->newData[k][DIV_PAT_NOTE]!=-1) { + pat->newData[k][DIV_PAT_FX(usedEffectsCol)+(l*2)]=0x00; + pat->newData[k][DIV_PAT_FXVAL(usedEffectsCol)+(l*2)]=0; chArpeggio[j]=false; - } else if (chPorta[j] && pat->data[k][4+(l*2)]!=0x03 && pat->data[k][4+(l*2)]!=0x01 && pat->data[k][4+(l*2)]!=0x02) { - pat->data[k][4+usedEffectsCol*2+(l*2)]=0x03; - pat->data[k][5+usedEffectsCol*2+(l*2)]=0; + } else if (chPorta[j] && pat->newData[k][DIV_PAT_FX(l)]!=0x03 && pat->newData[k][DIV_PAT_FX(l)]!=0x01 && pat->newData[k][DIV_PAT_FX(l)]!=0x02) { + pat->newData[k][DIV_PAT_FX(usedEffectsCol)+(l*2)]=0x03; + pat->newData[k][DIV_PAT_FXVAL(usedEffectsCol)+(l*2)]=0; chPorta[j]=false; - } else if (chVibrato[j] && pat->data[k][4+(l*2)]!=0x04 && pat->data[k][0]!=-1) { - pat->data[k][4+usedEffectsCol*2+(l*2)]=0x04; - pat->data[k][5+usedEffectsCol*2+(l*2)]=0; + } else if (chVibrato[j] && pat->newData[k][DIV_PAT_FX(l)]!=0x04 && pat->newData[k][DIV_PAT_NOTE]!=-1) { + pat->newData[k][DIV_PAT_FX(usedEffectsCol)+(l*2)]=0x04; + pat->newData[k][DIV_PAT_FXVAL(usedEffectsCol)+(l*2)]=0; chVibrato[j]=false; - } else if (chVolumeSlide[j] && pat->data[k][4+(l*2)]!=0x0A) { - pat->data[k][4+usedEffectsCol*2+(l*2)]=0x0A; - pat->data[k][5+usedEffectsCol*2+(l*2)]=0; + } else if (chVolumeSlide[j] && pat->newData[k][DIV_PAT_FX(l)]!=0x0A) { + pat->newData[k][DIV_PAT_FX(usedEffectsCol)+(l*2)]=0x0A; + pat->newData[k][DIV_PAT_FXVAL(usedEffectsCol)+(l*2)]=0; chVolumeSlide[j]=false; } - switch (pat->data[k][4+l]) { + // TODO: looks like we have a bug here! it should be DIV_PAT_FX(l), right? + switch (pat->newData[k][DIV_PAT_FX(0)+l]) { case 0: chArpeggio[j]=true; break; @@ -527,16 +523,16 @@ void TFMParsePattern(struct TFMParsePatternInfo info) { lastPat->copyOn(newPat); info.ds->subsong[0]->orders.ord[i][info.ds->subsong[0]->ordersLen - 1] = info.maxPat; - newPat->data[info.patLens[lastPatNum]-1][4+(usedEffectsCol*4)] = 0x0B; - newPat->data[info.patLens[lastPatNum]-1][5+(usedEffectsCol*4)] = info.loopPos; + newPat->newData[info.patLens[lastPatNum]-1][DIV_PAT_FX(usedEffectsCol*2)] = 0x0B; + newPat->newData[info.patLens[lastPatNum]-1][DIV_PAT_FXVAL(usedEffectsCol*2)] = info.loopPos; info.ds->subsong[0]->pat[i].data[info.maxPat] = newPat; } } else { for (int i=0;i<6;i++) { int lastPatNum=info.ds->subsong[0]->orders.ord[i][info.ds->subsong[0]->ordersLen - 1]; DivPattern* lastPat=info.ds->subsong[0]->pat[i].getPattern(lastPatNum, false); - lastPat->data[info.patLens[lastPatNum]-1][4+(usedEffectsCol*4)] = 0x0B; - lastPat->data[info.patLens[lastPatNum]-1][5+(usedEffectsCol*4)] = info.loopPos; + lastPat->newData[info.patLens[lastPatNum]-1][DIV_PAT_FX(usedEffectsCol*2)] = 0x0B; + lastPat->newData[info.patLens[lastPatNum]-1][DIV_PAT_FXVAL(usedEffectsCol*2)] = info.loopPos; } } } diff --git a/src/engine/fileOps/xm.cpp b/src/engine/fileOps/xm.cpp index edc1cf14c..bf6ac53a0 100644 --- a/src/engine/fileOps/xm.cpp +++ b/src/engine/fileOps/xm.cpp @@ -786,7 +786,7 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { bool mustCommitInitial=true; - memset(effectCol,4,128); + memset(effectCol,0,128); memset(vibStatus,0,128); memset(vibStatusChanged,0,128*sizeof(bool)); memset(vibing,0,128*sizeof(bool)); @@ -901,32 +901,26 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { if (note!=0) { lastNote[k]=note; if (note>96) { - p->data[j][0]=101; - p->data[j][1]=0; + p->newData[j][DIV_PAT_NOTE]=DIV_NOTE_REL; } else { note--; - p->data[j][0]=note%12; - p->data[j][1]=note/12; - if (p->data[j][0]==0) { - p->data[j][0]=12; - p->data[j][1]=(unsigned char)(p->data[j][1]-1); - } + p->newData[j][DIV_PAT_NOTE]=note+60; } } } if (hasIns) { ins=reader.readC(); - p->data[j][2]=((int)ins)-1; + p->newData[j][DIV_PAT_INS]=((int)ins)-1; // default volume if (lastNote[k]<96 && ins>0) { - p->data[j][3]=sampleVol[(((ins-1)&255)<<8)|(noteMap[(((ins-1)&255)<<7)|(lastNote[k]&127)])]; + p->newData[j][DIV_PAT_VOL]=sampleVol[(((ins-1)&255)<<8)|(noteMap[(((ins-1)&255)<<7)|(lastNote[k]&127)])]; } writePanning=true; } if (hasVol) { vol=reader.readC(); if (vol>=0x10 && vol<=0x50) { - p->data[j][3]=vol-0x10; + p->newData[j][DIV_PAT_VOL]=vol-0x10; } else { // effects in volume column switch (vol>>4) { case 0x6: // vol slide down @@ -986,11 +980,11 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { vibing[k]=true; break; case 0xc: // panning - p->data[j][effectCol[k]++]=0x80; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x80; if ((vol&15)==8) { - p->data[j][effectCol[k]++]=0x80; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x80; } else { - p->data[j][effectCol[k]++]=(vol&15)|((vol&15)<<4); + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=(vol&15)|((vol&15)<<4); } writePanning=false; break; @@ -1113,14 +1107,14 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { treming[k]=true; break; case 8: // panning - p->data[j][effectCol[k]++]=0x80; - p->data[j][effectCol[k]++]=effectVal; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x80; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal; writePanning=false; break; case 9: // offset if (hasNote) { - p->data[j][effectCol[k]++]=0x91; - p->data[j][effectCol[k]++]=effectVal; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x91; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal; } break; case 0xa: // vol slide @@ -1134,15 +1128,15 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { volSliding[k]=true; break; case 0xb: // go to order - p->data[j][effectCol[k]++]=0x0b; - p->data[j][effectCol[k]++]=effectVal; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0b; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal; break; case 0xc: // set volume - p->data[j][3]=effectVal; + p->newData[j][DIV_PAT_VOL]=effectVal; break; case 0xd: // next order - p->data[j][effectCol[k]++]=0x0d; - p->data[j][effectCol[k]++]=effectVal; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0d; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal; break; case 0xe: // special... // TODO: implement the rest @@ -1150,27 +1144,27 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { case 0x4: // vibrato waveform switch (effectVal&3) { case 0x0: // sine - p->data[j][effectCol[k]++]=0xe3; - p->data[j][effectCol[k]++]=0x00; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe3; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x00; break; case 0x1: // ramp down - p->data[j][effectCol[k]++]=0xe3; - p->data[j][effectCol[k]++]=0x05; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe3; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x05; break; case 0x2: // square case 0x3: - p->data[j][effectCol[k]++]=0xe3; - p->data[j][effectCol[k]++]=0x06; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe3; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x06; break; } break; case 0x5: // fine tune - p->data[j][effectCol[k]++]=0xe5; - p->data[j][effectCol[k]++]=(effectVal&15)<<4; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe5; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=(effectVal&15)<<4; break; case 0x9: // retrigger - p->data[j][effectCol[k]++]=0x0c; - p->data[j][effectCol[k]++]=(effectVal&15); + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0c; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=(effectVal&15); break; case 0xa: // vol slide up (fine) volSlideStatus[k]=((effectVal&15)<<4)|0xf; @@ -1189,32 +1183,32 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { volSliding[k]=true; break; case 0xc: // note cut - p->data[j][effectCol[k]++]=0xdc; - p->data[j][effectCol[k]++]=MAX(1,effectVal&15); + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xdc; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=MAX(1,effectVal&15); break; case 0xd: // note delay - p->data[j][effectCol[k]++]=0xed; - p->data[j][effectCol[k]++]=MAX(1,effectVal&15); + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xed; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=MAX(1,effectVal&15); break; } break; case 0xf: // speed/tempo if (effectVal>=0x20) { - p->data[j][effectCol[k]++]=0xf0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xf0; } else if (effectVal==0) { - p->data[j][effectCol[k]++]=0xff; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xff; } else { - p->data[j][effectCol[k]++]=0x0f; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0f; } - p->data[j][effectCol[k]++]=effectVal; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal; break; case 0x10: // G: global volume (!) break; case 0x11: // H: global volume slide (!) break; case 0x14: // K: key off - p->data[j][effectCol[k]++]=0xe7; - p->data[j][effectCol[k]++]=effectVal; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xe7; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal; break; case 0x15: // L: set envelope position (!) break; @@ -1226,8 +1220,8 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { panSliding[k]=true; break; case 0x1b: // R: retrigger - p->data[j][effectCol[k]++]=0x0c; - p->data[j][effectCol[k]++]=effectVal; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x0c; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=effectVal; break; case 0x1d: // T: tremor (!) break; @@ -1244,8 +1238,8 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { } if (writePanning && hasNote && note<96 && ins>0) { - p->data[j][effectCol[k]++]=0x80; - p->data[j][effectCol[k]++]=samplePan[(((ins-1)&255)<<8)|(noteMap[(((ins-1)&255)<<7)|(note&127)])]; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x80; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=samplePan[(((ins-1)&255)<<8)|(noteMap[(((ins-1)&255)<<7)|(note&127)])]; } } @@ -1253,80 +1247,80 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { for (int k=0; kpat[k].getPattern(i,true); if (vibing[k]!=vibingOld[k] || vibStatusChanged[k]) { - p->data[j][effectCol[k]++]=0x04; - p->data[j][effectCol[k]++]=vibing[k]?vibStatus[k]:0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x04; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=vibing[k]?vibStatus[k]:0; doesVibrato[k]=true; } else if (doesVibrato[k] && mustCommitInitial) { - p->data[j][effectCol[k]++]=0x04; - p->data[j][effectCol[k]++]=0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x04; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0; } if (volSliding[k]!=volSlidingOld[k] || volSlideStatusChanged[k]) { if (volSlideStatus[k]>=0xf1 && volSliding[k]) { - p->data[j][effectCol[k]++]=0xf9; - p->data[j][effectCol[k]++]=volSlideStatus[k]&15; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xf9; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=volSlideStatus[k]&15; volSliding[k]=false; } else if ((volSlideStatus[k]&15)==15 && volSlideStatus[k]>=0x10 && volSliding[k]) { - p->data[j][effectCol[k]++]=0xf8; - p->data[j][effectCol[k]++]=volSlideStatus[k]>>4; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xf8; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=volSlideStatus[k]>>4; volSliding[k]=false; } else { - p->data[j][effectCol[k]++]=0xfa; - p->data[j][effectCol[k]++]=volSliding[k]?volSlideStatus[k]:0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xfa; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=volSliding[k]?volSlideStatus[k]:0; } doesVolSlide[k]=true; } else if (doesVolSlide[k] && mustCommitInitial) { - p->data[j][effectCol[k]++]=0xfa; - p->data[j][effectCol[k]++]=0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0xfa; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0; } if (porting[k]!=portingOld[k] || portaStatusChanged[k]) { - p->data[j][effectCol[k]++]=portaType[k]; - p->data[j][effectCol[k]++]=porting[k]?portaStatus[k]:0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=portaType[k]; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=porting[k]?portaStatus[k]:0; doesPitchSlide[k]=true; } else if (doesPitchSlide[k] && mustCommitInitial) { - p->data[j][effectCol[k]++]=0x01; - p->data[j][effectCol[k]++]=0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x01; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0; } if (arping[k]!=arpingOld[k] || arpStatusChanged[k]) { - p->data[j][effectCol[k]++]=0x00; - p->data[j][effectCol[k]++]=arping[k]?arpStatus[k]:0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x00; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=arping[k]?arpStatus[k]:0; doesArp[k]=true; } else if (doesArp[k] && mustCommitInitial) { - p->data[j][effectCol[k]++]=0x00; - p->data[j][effectCol[k]++]=0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x00; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0; } if (treming[k]!=tremingOld[k] || tremStatusChanged[k]) { - p->data[j][effectCol[k]++]=0x07; - p->data[j][effectCol[k]++]=treming[k]?tremStatus[k]:0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x07; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=treming[k]?tremStatus[k]:0; doesTremolo[k]=true; } else if (doesTremolo[k] && mustCommitInitial) { - p->data[j][effectCol[k]++]=0x07; - p->data[j][effectCol[k]++]=0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x07; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0; } if (panning[k]!=panningOld[k] || panStatusChanged[k]) { - p->data[j][effectCol[k]++]=0x84; - p->data[j][effectCol[k]++]=panning[k]?panStatus[k]:0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x84; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=panning[k]?panStatus[k]:0; doesPanbrello[k]=true; } else if (doesPanbrello[k] && mustCommitInitial) { - p->data[j][effectCol[k]++]=0x84; - p->data[j][effectCol[k]++]=0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x84; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0; } if (panSliding[k]!=panSlidingOld[k] || panSlideStatusChanged[k]) { - p->data[j][effectCol[k]++]=0x83; - p->data[j][effectCol[k]++]=panSliding[k]?panSlideStatus[k]:0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x83; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=panSliding[k]?panSlideStatus[k]:0; doesPanSlide[k]=true; } else if (doesPanSlide[k] && mustCommitInitial) { - p->data[j][effectCol[k]++]=0x83; - p->data[j][effectCol[k]++]=0; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0x83; + p->newData[j][DIV_PAT_FX(0)+effectCol[k]++]=0; } - if ((effectCol[k]>>1)-2>ds.subsong[0]->pat[k].effectCols) { - ds.subsong[0]->pat[k].effectCols=(effectCol[k]>>1)-1; + if ((effectCol[k]>>1)>=ds.subsong[0]->pat[k].effectCols) { + ds.subsong[0]->pat[k].effectCols=(effectCol[k]>>1)+1; } } @@ -1346,14 +1340,14 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { if (j==totalRows-1) { // place end of pattern marker DivPattern* p=ds.subsong[0]->pat[0].getPattern(i,true); - p->data[j][effectCol[0]++]=0x0d; - p->data[j][effectCol[0]++]=0; + p->newData[j][DIV_PAT_FX(0)+effectCol[0]++]=0x0d; + p->newData[j][DIV_PAT_FX(0)+effectCol[0]++]=0; - if ((effectCol[0]>>1)-2>ds.subsong[0]->pat[0].effectCols) { - ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)-1; + if ((effectCol[0]>>1)>=ds.subsong[0]->pat[0].effectCols) { + ds.subsong[0]->pat[0].effectCols=(effectCol[0]>>1)+1; } } - memset(effectCol,4,64); + memset(effectCol,0,64); } logV("seeking to %x...",packedSeek); diff --git a/src/engine/pattern.cpp b/src/engine/pattern.cpp index 49e66665c..cce45e65c 100644 --- a/src/engine/pattern.cpp +++ b/src/engine/pattern.cpp @@ -45,7 +45,7 @@ std::vector> DivChannelData::optimize() { for (int j=0; jdata,data[j]->data,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short))==0) { + if (memcmp(data[i]->newData,data[j]->newData,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short))==0) { delete data[j]; data[j]=NULL; logV("%d == %d",i,j); @@ -86,15 +86,11 @@ void DivChannelData::wipePatterns() { void DivPattern::copyOn(DivPattern* dest) { dest->name=name; - memcpy(dest->data,data,sizeof(data)); + memcpy(dest->newData,newData,sizeof(newData)); } void DivPattern::clear() { - memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short)); - for (int i=0; i #define _USE_MATH_DEFINES @@ -26,22 +31,27 @@ #include "../ta-log.h" #include +// go to next order void DivEngine::nextOrder() { curRow=0; if (repeatPattern) return; if (++curOrder>=curSubSong->ordersLen) { logV("end of orders reached"); endOfSong=true; + // the walked array is used for loop detection + // since we've reached the end, we are guaranteed to loop here, so + // just reset it. memset(walked,0,8192); curOrder=0; } } +// used for the pattern visualizer in console mode. static const char* notes[12]={ "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" }; -// update this when adding new commands. +// update this when adding new commands in dispatch.h. const char* cmdName[]={ "NOTE_ON", "NOTE_OFF", @@ -318,25 +328,32 @@ const char* cmdName[]={ "FM_AMS2" }; +// fail build if you forgot to update the array static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!"); -const char* formatNote(unsigned char note, unsigned char octave) { - static char ret[4]; - if (note==100) { +// formats a note +// used for the pattern visualizer in console mode, justifying the use +// of a static array. +const char* formatNote(short note) { + static char ret[16]; + if (note==DIV_NOTE_OFF) { return "OFF"; - } else if (note==101) { + } else if (note==DIV_NOTE_REL) { return "==="; - } else if (note==102) { + } else if (note==DIV_MACRO_REL) { return "REL"; - } else if (octave==0 && note==0) { + } else if (note<0) { return "---"; } - snprintf(ret,4,"%s%d",notes[note%12],octave+note/12); + snprintf(ret,16,"%s%d",notes[note%12],(note-60)/12); return ret; } +// send a command to a dispatch. int DivEngine::dispatchCmd(DivCommand c) { + // used for the commands visualizer in console mode if (view==DIV_STATUS_COMMANDS) { + // don't print if we are "skipping" (seeking to a position, usually after channel reset on loop) if (!skipping) { switch (c.cmd) { // strip away hinted/useless commands @@ -353,55 +370,73 @@ int DivEngine::dispatchCmd(DivCommand c) { case DIV_CMD_PRE_NOTE: break; default: + // print command printf("%8d | %d: %s(%d, %d)\n",totalTicksR,c.chan,cmdName[c.cmd],c.value,c.value2); } } } totalCmds++; + // up to 2000 commands can be queued in the command queue (used by the GUI for pattern visualizer) if (cmdStreamEnabled && cmdStream.size()<2000) { cmdStream.push_back(c); } + // MIDI output code + // we turn this command into MIDI messages if the output mode is "melodic" + // if the channel is outside the range 0-15, it will be wrapped back if (output) if (!skipping && output->midiOut!=NULL && !isChannelMuted(c.chan)) { if (output->midiOut->isDeviceOpen()) { if (midiOutMode==DIV_MIDI_MODE_NOTE) { + // scale volume to MIDI velocity range int scaledVol=(chan[c.chan].volume*127)/MAX(1,chan[c.chan].volMax); if (scaledVol<0) scaledVol=0; if (scaledVol>127) scaledVol=127; + + // process the command switch (c.cmd) { case DIV_CMD_NOTE_ON: case DIV_CMD_LEGATO: + // turn the previous note off (if we have one) if (chan[c.chan].curMidiNote>=0) { output->midiOut->send(TAMidiMessage(0x80|(c.chan&15),chan[c.chan].curMidiNote,scaledVol)); } + // set current MIDI note if (c.value!=DIV_NOTE_NULL) { chan[c.chan].curMidiNote=c.value+12; if (chan[c.chan].curMidiNote<0) chan[c.chan].curMidiNote=0; if (chan[c.chan].curMidiNote>127) chan[c.chan].curMidiNote=127; } + // send note on (if we have one) if (chan[c.chan].curMidiNote>=0) { output->midiOut->send(TAMidiMessage(0x90|(c.chan&15),chan[c.chan].curMidiNote,scaledVol)); } break; case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF_ENV: + // turn the current note off (if we have one) + // we don't do this for macro release... if (chan[c.chan].curMidiNote>=0) { output->midiOut->send(TAMidiMessage(0x80|(c.chan&15),chan[c.chan].curMidiNote,scaledVol)); } chan[c.chan].curMidiNote=-1; break; case DIV_CMD_INSTRUMENT: + // instrument changes mapped to program change + // only first 128 instruments if (chan[c.chan].lastIns!=c.value && midiOutProgramChange) { output->midiOut->send(TAMidiMessage(0xc0|(c.chan&15),c.value&0x7f,0)); } break; case DIV_CMD_VOLUME: + // volume changes are sent as MIDI aftertouch, as long as there isn't a note + // (processRow will set midiAftertouch to true on every row without note) if (chan[c.chan].curMidiNote>=0 && chan[c.chan].midiAftertouch) { chan[c.chan].midiAftertouch=false; output->midiOut->send(TAMidiMessage(0xa0|(c.chan&15),chan[c.chan].curMidiNote,scaledVol)); } break; case DIV_CMD_PITCH: { + // map pitch changes to pitch bend (including vibrato) int pitchBend=8192+(c.value<<5); if (pitchBend<0) pitchBend=0; if (pitchBend>16383) pitchBend=16383; @@ -412,6 +447,7 @@ int DivEngine::dispatchCmd(DivCommand c) { break; } case DIV_CMD_PANNING: { + // this is mapped to General MIDI panning CC int pan=convertPanSplitToLinearLR(c.value,c.value2,127); if (pan<0) pan=0; if (pan>127) pan=127; @@ -419,25 +455,40 @@ int DivEngine::dispatchCmd(DivCommand c) { break; } case DIV_CMD_HINT_PORTA: { + // portamento handling is complicated + // in General MIDI, portamento consists of sending a CC for duration and another for target note + // this differs from Furnace, where the parameter is speed rather than duration + // it is also impossible to perform an indefinite slide down/up other than + // by using pitch bend, but the default range is limited and we already + // use it for pitch changes/vibrato + + // only send portamento if it is enabling if (c.value2>0) { + // and only if we have a target note if (c.value<=0 || c.value>=255) break; //output->midiOut->send(TAMidiMessage(0x80|(c.chan&15),chan[c.chan].curMidiNote,scaledVol)); int target=c.value+12; if (target<0) target=0; if (target>127) target=127; + // set the source note? if (chan[c.chan].curMidiNote>=0) { output->midiOut->send(TAMidiMessage(0xb0|(c.chan&15),0x54,chan[c.chan].curMidiNote)); } + // set the duration + // no effort whatsoever is done to predict how long will the slide last output->midiOut->send(TAMidiMessage(0xb0|(c.chan&15),0x05,1/*MIN(0x7f,c.value2/4)*/)); + // turn portamento on output->midiOut->send(TAMidiMessage(0xb0|(c.chan&15),0x41,0x7f)); - + // send a note on (why?) output->midiOut->send(TAMidiMessage(0x90|(c.chan&15),target,scaledVol)); } else { + // disable portamento otherwise output->midiOut->send(TAMidiMessage(0xb0|(c.chan&15),0x41,0)); } break; } + // other commands are simply ignored default: break; } @@ -445,47 +496,61 @@ int DivEngine::dispatchCmd(DivCommand c) { } } + // map the channel to channel of chip + // c.dis is a copy of c.chan because we'll use it in the next call c.chan=dispatchChanOfChan[c.dis]; + // dispatch command to chip dispatch return disCont[dispatchOfChan[c.dis]].dispatch->dispatch(c); } +// this function handles per-chip normal effects bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effectVal) { + // don't process invalid chips DivSysDef* sysDef=sysDefs[sysOfChan[ch]]; if (sysDef==NULL) return false; + // find the effect handler auto iter=sysDef->effectHandlers.find(effect); if (iter==sysDef->effectHandlers.end()) return false; EffectHandler handler=iter->second; int val=0; int val2=0; + // map values using the handler's function try { val=handler.val?handler.val(effect,effectVal):effectVal; val2=handler.val2?handler.val2(effect,effectVal):0; } catch (DivDoNotHandleEffect& e) { return false; } + // dispatch command // wouldn't this cause problems if it were to return 0? return dispatchCmd(DivCommand(handler.dispatchCmd,ch,val,val2)); } +// this handles per-chip post effects... bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal) { + // don't process invalid chips DivSysDef* sysDef=sysDefs[sysOfChan[ch]]; if (sysDef==NULL) return false; + // find the effect handler auto iter=sysDef->postEffectHandlers.find(effect); if (iter==sysDef->postEffectHandlers.end()) return false; EffectHandler handler=iter->second; int val=0; int val2=0; + // map values using the handler's function try { val=handler.val?handler.val(effect,effectVal):effectVal; val2=handler.val2?handler.val2(effect,effectVal):0; } catch (DivDoNotHandleEffect& e) { return true; } + // dispatch command // wouldn't this cause problems if it were to return 0? return dispatchCmd(DivCommand(handler.dispatchCmd,ch,val,val2)); } +// ...and this handles chip pre-effects bool DivEngine::perSystemPreEffect(int ch, unsigned char effect, unsigned char effectVal) { DivSysDef* sysDef=sysDefs[sysOfChan[ch]]; if (sysDef==NULL) return false; @@ -504,39 +569,64 @@ bool DivEngine::perSystemPreEffect(int ch, unsigned char effect, unsigned char e return dispatchCmd(DivCommand(handler.dispatchCmd,ch,val,val2)); } +// this is called by nextRow() before it calls processRow() +// `i` is the channel void DivEngine::processRowPre(int i) { int whatOrder=curOrder; int whatRow=curRow; DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][whatOrder],false); + // check all effects for (int j=0; jdata[whatRow][4+(j<<1)]; - short effectVal=pat->data[whatRow][5+(j<<1)]; + short effect=pat->newData[whatRow][DIV_PAT_FX(j)]; + short effectVal=pat->newData[whatRow][DIV_PAT_FXVAL(j)]; + // empty effect value is the same as zero if (effectVal==-1) effectVal=0; effectVal&=255; + + // per-chip pre-effects (that's it for now!) + // the other pre-effects are handled in processRow() perSystemPreEffect(i,effect,effectVal); } } +// this is called by nextRow() or nextTick() (in the case of delay). it processes the next row for a channel. +// `i` is the channel and `afterDelay` determines whether this happens after EDxx or not. +// the processing order is: +// 1. pre-effects (delay and song control) +// 2. instrument +// 3. note reading (note off is done immediately) +// 4. volume +// 5. effects +// 6. note on +// 7. post-effects void DivEngine::processRow(int i, bool afterDelay) { + // if this is after delay, use the order/row where delay occurred int whatOrder=afterDelay?chan[i].delayOrder:curOrder; int whatRow=afterDelay?chan[i].delayRow:curRow; DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][whatOrder],false); // pre effects + // these include song control ones such as speed, tempo or jumps which shall not be delayed + // it also includes EDxx (delay) itself so we can handle it if (!afterDelay) { + // set to true if we found an EDxx effect bool returnAfterPre=false; + // check all effects for (int j=0; jdata[whatRow][4+(j<<1)]; - short effectVal=pat->data[whatRow][5+(j<<1)]; + short effect=pat->newData[whatRow][DIV_PAT_FX(j)]; + short effectVal=pat->newData[whatRow][DIV_PAT_FXVAL(j)]; + // empty effect value is the same as zero if (effectVal==-1) effectVal=0; effectVal&=255; switch (effect) { case 0x09: // select groove pattern/speed 1 if (song.grooves.empty()) { + // special case: sets speed 1 if the song lacks groove patterns if (effectVal>0) speeds.val[0]=effectVal; } else { + // sets the groove pattern and resets current speed index if (effectVal<(short)song.grooves.size()) { speeds=song.grooves[effectVal]; curSpeed=0; @@ -544,9 +634,12 @@ void DivEngine::processRow(int i, bool afterDelay) { } break; case 0x0f: // speed 1/speed 2 + // if the value is 0 then ignore it if (speeds.len==2 && song.grooves.empty()) { + // if there are two speeds and no groove patterns, set the second speed if (effectVal>0) speeds.val[1]=effectVal; } else { + // otherwise set the first speed if (effectVal>0) speeds.val[0]=effectVal; } break; @@ -557,6 +650,15 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal>0) virtualTempoD=effectVal; break; case 0x0b: // change order + // this actually schedules an order change + // we perform this change at the end of nextRow() + + // COMPAT FLAG: simultaneous jump treatment + // - 0: normal (another 0Bxx effect will override the previous one) + // - 1: old Furnace (only the first 0Bxx effect in a row takes effect) + // - 2: DefleMask (same as 1) + // in the case of normal, the jump row (changePos) is not reset to 0 + // this means that you can do 0Dxx 0Byy and it'll work, taking you to row xx of order yy if (changeOrd==-1 || song.jumpTreatment==0) { changeOrd=effectVal; if (song.jumpTreatment==1 || song.jumpTreatment==2) { @@ -565,18 +667,28 @@ void DivEngine::processRow(int i, bool afterDelay) { } break; case 0x0d: // next order + // COMPAT FLAG: ignore 0Dxx on the last order (ignoreJumpAtEnd) + // if there is a 0Dxx effect on the very last order, it is ignored + + // COMPAT FLAG: simultaneous jump treatment if (song.jumpTreatment==2) { + // - 2: DefleMask (jump to next order unless it is the last one and ignoreJumpAtEnd is on) if ((curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) { + // changeOrd -2 means increase order by 1 + // it overrides a previous 0Bxx effect changeOrd=-2; changePos=effectVal; } } else if (song.jumpTreatment==1) { + // - 1: old Furnace (same as 2 but ignored if 0Bxx is present) if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) { changeOrd=-2; changePos=effectVal; } } else { + // - 0: normal if (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd) { + // set the target order if not set, allowing you to use 0B and 0D regardless of position if (changeOrd<0) { changeOrd=-2; } @@ -586,17 +698,35 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xed: // delay if (effectVal!=0) { + // COMPAT FLAG: cut/delay effect policy (delayBehavior) + // - 0: strict + // - delays equal or greater to the speed * timeBase are ignored + // - 1: strict old + // - delays equal or greater to the speed are ignored + // - 2: lax (default) + // - no delay is ever ignored unless overridden by another bool comparison=(song.delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<(nextSpeed*(curSubSong->timeBase+1))); if (song.delayBehavior==2) comparison=true; if (comparison) { + // set the delay row, order and timer chan[i].rowDelay=effectVal; chan[i].delayOrder=whatOrder; chan[i].delayRow=whatRow; + + // this here was a compatibility hack for DefleMask... + // if the delay time happens to be equal to the speed, it'll + // result in "delay lock" which halts all row processing + // until another good EDxx effect is found + // for some reason this didn't occur on Neo Geo... + // this hack is disabled due to its dirtiness and the fact I + // don't feel like being compatible with a buggy tracker any further if (effectVal==nextSpeed) { //if (sysOfChan[i]!=DIV_SYSTEM_YM2610 && sysOfChan[i]!=DIV_SYSTEM_YM2610_EXT) chan[i].delayLocked=true; } else { chan[i].delayLocked=false; } + + // once we're done with pre-effects, get out and don't process any further returnAfterPre=true; } else { logV("higher than nextSpeed! %d>%d",effectVal,nextSpeed); @@ -606,20 +736,27 @@ void DivEngine::processRow(int i, bool afterDelay) { break; } } + // stop processing if EDxx was found if (returnAfterPre) return; } else { //logV("honoring delay at position %d",whatRow); } + // stop processing if delay lock is on (won't happen, ever) if (chan[i].delayLocked) return; + // now we start reading... // instrument bool insChanged=false; - if (pat->data[whatRow][2]!=-1) { - 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]; + if (pat->newData[whatRow][DIV_PAT_INS]!=-1) { + // only send an instrument change if it differs from the current ins + if (chan[i].lastIns!=pat->newData[whatRow][DIV_PAT_INS]) { + dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,pat->newData[whatRow][DIV_PAT_INS])); + chan[i].lastIns=pat->newData[whatRow][DIV_PAT_INS]; insChanged=true; + + // COMPAT FLAG: legacy volume slides + // - sets volume to max once a vol slide down has finished (thus setting volume to volMax+1) if (song.legacyVolumeSlides && chan[i].volume==chan[i].volMax+1) { logV("forcing volume"); chan[i].volume=chan[i].volMax; @@ -628,34 +765,46 @@ void DivEngine::processRow(int i, bool afterDelay) { } } } - // note - if (pat->data[whatRow][0]==100) { // note off - //chan[i].note=-1; + + // note reading + // note offs are sent immediately + if (pat->newData[whatRow][DIV_PAT_NOTE]==DIV_NOTE_OFF) { // note off chan[i].keyOn=false; chan[i].keyOff=true; + + // COMPAT FLAG: reset slides on note off (inverted in the GUI) + // - a portamento/pitch slide will be halted upon encountering note off + // - this will not occur if the stopPortaOnNoteOff flag is on and this is a portamento if (chan[i].inPorta && song.noteOffResetsSlides) { + // stopOnOff will be false if stopPortaOnNoteOff flag is off if (chan[i].stopOnOff) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); chan[i].stopOnOff=false; } + // depending on the system, portamento may still be disabled if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); + // this here is a now-disabled hack which makes the noise channel also stop when square 3 is /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { chan[i+1].portaNote=-1; chan[i+1].portaSpeed=-1; }*/ } + // another compatibility hack which schedules a second reset later just in case chan[i].scheduledSlideReset=true; } + + // send note off dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i)); - } else if (pat->data[whatRow][0]==101) { // note off + env release + } else if (pat->newData[whatRow][DIV_PAT_NOTE]==DIV_NOTE_REL) { // note off + env release //chan[i].note=-1; chan[i].keyOn=false; chan[i].keyOff=true; + // same thing here regarding reset slide behavior if (chan[i].inPorta && song.noteOffResetsSlides) { if (chan[i].stopOnOff) { chan[i].portaNote=-1; @@ -674,21 +823,30 @@ void DivEngine::processRow(int i, bool afterDelay) { } chan[i].scheduledSlideReset=true; } + + // send note release dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,i)); chan[i].releasing=true; - } else if (pat->data[whatRow][0]==102) { // env release + } else if (pat->newData[whatRow][DIV_PAT_NOTE]==DIV_MACRO_REL) { // env release + // send macro release dispatchCmd(DivCommand(DIV_CMD_ENV_RELEASE,i)); chan[i].releasing=true; - } else if (!(pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0)) { + } else if (pat->newData[whatRow][DIV_PAT_NOTE]!=-1) { + // prepare/schedule a new note chan[i].oldNote=chan[i].note; - chan[i].note=pat->data[whatRow][0]+((signed char)pat->data[whatRow][1])*12; + chan[i].note=pat->newData[whatRow][DIV_PAT_NOTE]-60; + // I have no idea why is this check here since keyOn is guaranteed to be false at this point + // ...unless there's a way to trigger keyOn twice if (!chan[i].keyOn) { + // the behavior of arpeggio reset upon note off varies per system 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; + // COMPAT FLAG: compatible arpeggio + // - once a new note plays, arp will not be applied for this tick if (chan[i].arp!=0 && song.compatibleArpeggio) { chan[i].arpYield=true; } @@ -697,12 +855,14 @@ void DivEngine::processRow(int i, bool afterDelay) { // volume int volPortaTarget=-1; bool noApplyVolume=false; + // here we read all effects and check for a volume slide with target/volume "portamento"/"scivolando" (a term I invented as an equivalent) for (int j=0; jdata[whatRow][4+(j<<1)]; + short effect=pat->newData[whatRow][DIV_PAT_FX(j)]; if (effect==0xd3 || effect==0xd4) { // vol porta - volPortaTarget=pat->data[whatRow][3]<<8; // can be -256 + volPortaTarget=pat->newData[whatRow][DIV_PAT_VOL]<<8; // can be -256 - short effectVal=pat->data[whatRow][5+(j<<1)]; + // empty effect value is treated as 0 + short effectVal=pat->newData[whatRow][DIV_PAT_FXVAL(j)]; if (effectVal==-1) effectVal=0; effectVal&=255; @@ -711,19 +871,28 @@ void DivEngine::processRow(int i, bool afterDelay) { } } - if (pat->data[whatRow][3]!=-1 && !noApplyVolume) { - if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { - if (pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0) { + // don't apply volume if a scivolando is set + if (pat->newData[whatRow][DIV_PAT_VOL]!=-1 && !noApplyVolume) { + // COMPAT FLAG: legacy ALWAYS_SET_VOLUME behavior (oldAlwaysSetVolume) + // - prior to its addition, volume changes wouldn't be effective depending on the system if the volume is the same as the current one + // - afterwards, volume change is made regardless in order to set the bottom byte of volume ("subvolume") + if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->newData[whatRow][DIV_PAT_VOL]) { + // here we let dispatchCmd() know we can do MIDI aftertouch if there isn't a note + if (pat->newData[whatRow][DIV_PAT_NOTE]==-1) { chan[i].midiAftertouch=true; } - chan[i].volume=pat->data[whatRow][3]<<8; + // set the volume (bottom byte is set to 0) + chan[i].volume=pat->newData[whatRow][DIV_PAT_VOL]<<8; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } } + // reset retrigger status + // this is the only effect that takes place only in the row it is placed, in a ProTracker-like fashion (why?) chan[i].retrigSpeed=0; + // stuff necessary for effect processing short lastSlide=-1; bool calledPorta=false; bool panChanged=false; @@ -732,20 +901,25 @@ void DivEngine::processRow(int i, bool afterDelay) { // effects for (int j=0; jdata[whatRow][4+(j<<1)]; - short effectVal=pat->data[whatRow][5+(j<<1)]; + short effect=pat->newData[whatRow][DIV_PAT_FX(j)]; + short effectVal=pat->newData[whatRow][DIV_PAT_FXVAL(j)]; + // an empty effect value is treated as zero if (effectVal==-1) effectVal=0; effectVal&=255; // per-system effect + // if there isn't one, go through normal effects if (!perSystemEffect(i,effect,effectVal)) switch (effect) { + /// PANNING case 0x08: // panning (split 4-bit) chan[i].panL=(effectVal>>4)|(effectVal&0xf0); chan[i].panR=(effectVal&15)|((effectVal&15)<<4); + // panning command isn't sent until later panChanged=true; break; case 0x80: { // panning (linear) + // convert to splir unsigned short pan=convertPanLinearToSplit(effectVal,8,255); chan[i].panL=pan>>8; chan[i].panR=pan&0xff; @@ -762,6 +936,7 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0x83: // pan slide if (effectVal!=0) { + // set the pan slide speed if ((effectVal&15)!=0) { chan[i].panSpeed=(effectVal&15); } else { @@ -774,6 +949,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].panSpeed=0; } + // send hint (for command stream export) dispatchCmd(DivCommand(DIV_CMD_HINT_PAN_SLIDE,i,chan[i].panSpeed&0xff)); break; case 0x84: // panbrello @@ -783,8 +959,10 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].panDepth=effectVal&15; chan[i].panRate=effectVal>>4; if (chan[i].panDepth!=0) { + // panbrello and slides are incompatible chan[i].panSpeed=0; } + // send hint (for command stream export) dispatchCmd(DivCommand(DIV_CMD_HINT_PANBRELLO,i,effectVal)); break; case 0x88: // panning rear (split 4-bit) @@ -800,7 +978,14 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].panRR=effectVal; surroundPanChanged=true; break; - case 0x01: // ramp up + /// PITCH and more + // internally, slides and portamento share the same variables + case 0x01: // pitch slide up + // COMPAT FLAG: ignore duplicate slides + // - only the first 01xx effect is considered + // - 02xx still works + // - a previous portamento (03xx) will prevent this slide from occurring + // - E1xy/E2xy also will if *another* flag is set if (song.ignoreDuplicateSlides && (lastSlide==0x01 || lastSlide==0x1337)) break; lastSlide=0x01; if (effectVal==0) { @@ -808,21 +993,39 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].portaSpeed=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); chan[i].inPorta=false; + // COMPAT FLAG: arpeggio inhibits non-porta slides + // - the PRE_PORTA command is used to let the dispatch know we're entering a pitch slide + // - this prompts dispatch to stop processing arp macros during a slide + // - this only happens if pitch linearity is set to None + // - if we don't let dispatch know, the slide will never occur as arp takes over if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { + // COMPAT FLAG: limit slide range + // - this confines pitch slides from dispatch->getPortaFloor to C-8 (I think) + // - yep, the lowest portamento note depends on the system... chan[i].portaNote=song.limitSlides?0x60:255; chan[i].portaSpeed=effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); + // most of these are used for compat flag handling chan[i].portaStop=true; - chan[i].nowYouCanStop=false; chan[i].stopOnOff=false; chan[i].scheduledSlideReset=false; chan[i].wasShorthandPorta=false; chan[i].inPorta=false; + // COMPAT FLAG: arpeggio inhibits non-porta slides + // - the PRE_PORTA command is used to let the dispatch know we're entering a pitch slide + // - this prompts dispatch to stop processing arp macros during a slide + // - this only happens if pitch linearity is set to None + // - if we don't let dispatch know, the slide will never occur as arp takes over if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); } break; - case 0x02: // ramp down + case 0x02: // pitch slide down + // COMPAT FLAG: ignore duplicate slides + // - only the first 02xx effect is considered + // - 01xx still works + // - a previous portamento (03xx) will prevent this slide from occurring + // - E1xy/E2xy also will if *another* flag is set if (song.ignoreDuplicateSlides && (lastSlide==0x02 || lastSlide==0x1337)) break; lastSlide=0x02; if (effectVal==0) { @@ -830,21 +1033,27 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].portaSpeed=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); chan[i].inPorta=false; + // COMPAT FLAG: arpeggio inhibits non-porta slides if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { + // COMPAT FLAG: limit slide range + // - this confines pitch slides from dispatch->getPortaFloor to C-8 (I think) + // - yep, the lowest portamento note depends on the system... chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60; chan[i].portaSpeed=effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); chan[i].portaStop=true; - chan[i].nowYouCanStop=false; chan[i].stopOnOff=false; chan[i].scheduledSlideReset=false; chan[i].wasShorthandPorta=false; chan[i].inPorta=false; + // COMPAT FLAG: arpeggio inhibits non-porta slides if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); } break; case 0x03: // portamento + // exception: the arpNonPorta flag is not checked here. + // a portamento shall override arp macros on non-linear pitch. if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; @@ -852,34 +1061,54 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].inPorta=false; dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { + // lastPorta is used for the 06xy effect chan[i].lastPorta=effectVal; + // this here is for a compatibility flag... calledPorta=true; + // COMPAT FLAG: buggy portamento after sliding + // - you might want to slide up or down and then 03xx to return to the original note + // - if a porta to the same note is attempted after slide, for some reason it does not occur if (chan[i].note==chan[i].oldNote && !chan[i].inPorta && song.buggyPortaAfterSlide) { chan[i].portaNote=chan[i].note; chan[i].portaSpeed=-1; } else { + // compat flags get on my way chan[i].portaNote=chan[i].note; chan[i].portaSpeed=effectVal; chan[i].inPorta=true; + // ...but this one is for ANOTHER compat flag. yuck! chan[i].wasShorthandPorta=false; } dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); + // TODO; portaStop is guaranteed to be true anyway. what's the point of this? chan[i].portaStop=true; + // this is why we didn't send noye on before. + // there may be a portamento which of course prevents a note on if (chan[i].keyOn) chan[i].doNote=false; + // COMPAT FLAG: stop portamento on note off + // - if a portamento is called and then a note off occurs, stop portamento before the next note + // - ...unless noteOffResetsSlides is disabled chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! chan[i].scheduledSlideReset=false; dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,1)); + // this is used to inhibit any other slide commands if the respective compat flag is enabled lastSlide=0x1337; // i hate this so much } break; + // vibratos and pitch changes are mixed in. case 0x04: // vibrato + // remember the last vibrato for 05xy if (effectVal) chan[i].lastVibrato=effectVal; chan[i].vibratoDepth=effectVal&15; chan[i].vibratoRate=effectVal>>4; dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO,i,(chan[i].vibratoDepth&15)|(chan[i].vibratoRate<<4))); + // update pitch now dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); break; + /// VOLUME-RELATED case 0x05: // vol slide + vibrato + // this effect is weird. it shouldn't be here considering we have more + // than one effect column, but I guess it had to be done if (effectVal==0) { chan[i].vibratoDepth=0; chan[i].vibratoRate=0; @@ -888,6 +1117,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].vibratoRate=chan[i].lastVibrato>>4; } dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO,i,(chan[i].vibratoDepth&15)|(chan[i].vibratoRate<<4))); + // update pitch now dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); // TODO: non-0x-or-x0 value should be treated as 00 if (effectVal!=0) { @@ -906,6 +1136,7 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x06: // vol slide + porta + // same thing here. this is another effect that doesn't need to exist. if (effectVal==0 || chan[i].lastPorta==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; @@ -913,7 +1144,10 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].inPorta=false; dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { + // this here is for a compatibility flag... calledPorta=true; + // COMPAT FLAG: buggy portamento after sliding + // yes, this also affects 06xy. if (chan[i].note==chan[i].oldNote && !chan[i].inPorta && song.buggyPortaAfterSlide) { chan[i].portaNote=chan[i].note; chan[i].portaSpeed=-1; @@ -924,6 +1158,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].wasShorthandPorta=false; } dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); + // this is the same as 03xx. chan[i].portaStop=true; if (chan[i].keyOn) chan[i].doNote=false; chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! @@ -931,6 +1166,7 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,1)); lastSlide=0x1337; // i hate this so much } + // now handle volume slide // TODO: non-0x-or-x0 value should be treated as 00 if (effectVal!=0) { if ((effectVal&15)!=0) { @@ -948,21 +1184,28 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x07: // tremolo + // the implementation of tremolo in Furnace is more like that of + // vibrato. it oscillates according to a waveform. + // this differs from Defle where it is a consecutive vol slide + // up/down and exhibits a numbee of bugs. if (chan[i].tremoloDepth==0) { chan[i].tremoloPos=0; } chan[i].tremoloDepth=effectVal&15; chan[i].tremoloRate=effectVal>>4; dispatchCmd(DivCommand(DIV_CMD_HINT_TREMOLO,i,effectVal)); + // unfortunately, we cannot run both tremolo and vol slide at once. if (chan[i].tremoloDepth!=0) { chan[i].volSpeed=0; chan[i].volSpeedTarget=-1; } else { + // restore the volume if tremolo is disabled dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } break; - case 0x0a: // volume ramp + case 0x0a: // volume slide + // the speed multipler is 64, which means 4 ticks between volume changes with a value of 1 // TODO: non-0x-or-x0 value should be treated as 00 if (effectVal!=0) { if ((effectVal&15)!=0) { @@ -976,29 +1219,42 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + // reset the volume target chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; + /// NOTE case 0x00: // arpeggio chan[i].arp=effectVal; + // COMPAT FLAG: reset note to base on arp stop (inverted in the GUI) + // - a 0000 effect resets arpeggio position 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 + // this is the only non-continuous effect. it takes place exclusively + // within one row like most PC/Amiga trackers. + // consecutive 0Cxx effects will reset on each row... if (effectVal!=0) { chan[i].retrigSpeed=effectVal; chan[i].retrigTick=0; } break; + /// MISC case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: // set samp. pos + // COMPAT FLAG: old sample offset effect + // - before 0.6.3 the sample offset effect was 9xxx, where `xxx` is multiplied by 256 + // - the effect was then changed to 90xx/91xx/92xx, allowing you to set the low, mid and high bytes of the offset respectively if (song.oldSampleOffset) { + // send sample position now dispatchCmd(DivCommand(DIV_CMD_SAMPLE_POS,i,(((effect&0x0f)<<8)|effectVal)*256)); } else { + // change one byte and schedule sample position if (effect<0x93) { chan[i].sampleOff&=~(0xff<<((effect-0x90)<<3)); chan[i].sampleOff|=effectVal<<((effect-0x90)<<3); @@ -1007,6 +1263,11 @@ void DivEngine::processRow(int i, bool afterDelay) { } break; case 0xc0: case 0xc1: case 0xc2: case 0xc3: // set Hz + // Cxxx, where `xxx` is between 1 and 1023 + // divider is the tick rate in Hz + // cycles is the number of samples between ticks + // clockDrift is used for accuracy and subticks for low-latency mode + // (where we run faster thsn the tick rate to allow sub-tick note events from live playback) divider=(double)(((effect&0x3)<<8)|effectVal); if (divider<1) divider=1; cycles=got.rate/divider; @@ -1014,20 +1275,32 @@ void DivEngine::processRow(int i, bool afterDelay) { subticks=0; break; case 0xdc: // delayed mute + // used on XM import, where ECx actually mutes the note + // COMPAT FLAG: cut/delay effect policy (delayBehavior) + // - 0: strict + // - ignore cut if equal or greater than speed + // - 1: strict old + // - ignore cut if equal or greater than speed + // - 2: lax (default) + // - no cut is ever ignored unless overridden by another if (effectVal>0 && (song.delayBehavior==2 || effectValchan[i].volume ? effectVal : -effectVal; chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); break; case 0xd4: // volume portamento fast (vol porta fast) + // this is the same as D3xx, but 256 times faster. // tremolo and vol slides are incompatible chan[i].tremoloDepth=0; chan[i].tremoloRate=0; @@ -1036,46 +1309,63 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); break; case 0xe0: // arp speed + // the arp speed is global. I have no idea why. if (effectVal>0) { curSubSong->arpLen=effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_ARP_TIME,i,curSubSong->arpLen)); } break; case 0xe1: // portamento up + // this is a shortcut for 03xx and a higher note. + // it has the benefit of being able to be used in conjunction with a note. chan[i].portaNote=chan[i].note+(effectVal&15); chan[i].portaSpeed=(effectVal>>4)*4; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); + // these are for compatibility stuff chan[i].portaStop=true; - chan[i].nowYouCanStop=false; + // COMPAT FLAG: stop portamento on note off chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! chan[i].scheduledSlideReset=false; + // only enter portamento if the speed is set if ((effectVal&15)!=0) { chan[i].inPorta=true; + // these are for compatibility flaga. chan[i].shorthandPorta=true; chan[i].wasShorthandPorta=true; + // COMPAT FLAG: broken shortcut slides + // - oddly enough, shortcut slides are not communicated to the dispatch + // - this was fixed in 0.5.7 if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); + // COMPAT FLAG: E1xy/E2xy also take priority over slides + // - another Defle hack. it places shortcut slides above pitch slides. if (song.e1e2AlsoTakePriority) lastSlide=0x1337; // ... } else { chan[i].inPorta=false; + // COMPAT FLAG: broken shortcut slides if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } break; case 0xe2: // portamento down + // this is the same as E1xy but in the opposite direction. chan[i].portaNote=chan[i].note-(effectVal&15); chan[i].portaSpeed=(effectVal>>4)*4; dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0))); chan[i].portaStop=true; - chan[i].nowYouCanStop=false; + // COMPAT FLAG: stop portamento on note off chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! chan[i].scheduledSlideReset=false; if ((effectVal&15)!=0) { chan[i].inPorta=true; + // these are for compatibility flaga. chan[i].shorthandPorta=true; chan[i].wasShorthandPorta=true; + // COMPAT FLAG: broken shortcut slides if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); + // COMPAT FLAG: E1xy/E2xy also take priority over slides if (song.e1e2AlsoTakePriority) lastSlide=0x1337; // ... } else { chan[i].inPorta=false; + // COMPAT FLAG: broken shortcut slides if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } break; @@ -1084,21 +1374,23 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO_SHAPE,i,chan[i].vibratoShape)); break; case 0xe4: // vibrato fine + // this sets the multiplier for vibrato depth chan[i].vibratoFine=effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO_RANGE,i,chan[i].vibratoFine)); break; case 0xe5: // pitch chan[i].pitch=effectVal-0x80; + // why isn't this removed yet? we shpuldn't give this chip special treatment! if (sysOfChan[i]==DIV_SYSTEM_YM2151) { // YM2151 pitch oddity chan[i].pitch*=2; if (chan[i].pitch<-128) chan[i].pitch=-128; if (chan[i].pitch>127) chan[i].pitch=127; } - //chan[i].pitch+=globalPitch; + // send pitch now 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 0xe6: // Delayed legato + case 0xe6: // delayed legato // why does this have to follow FamiTracker verbatim // couldn't you do better? if ((effectVal&15)!=0) { @@ -1114,8 +1406,16 @@ void DivEngine::processRow(int i, bool afterDelay) { } break; case 0xe7: // delayed macro release + // COMPAT FLAG: cut/delay effect policy (delayBehavior) + // - 0: strict + // - ignore cut if equal or greater than speed + // - 1: strict old + // - ignore cut if equal or greater than speed + // - 2: lax (default) + // - no cut is ever ignored unless overridden by another // "Bruh" if (effectVal>0 && (song.delayBehavior==2 || effectVal0 && (song.delayBehavior==2 || effectVal