Merge branch 'master' into newMixer

This commit is contained in:
Eknous-P 2025-10-20 16:43:27 +04:00
commit 678a095822
96 changed files with 2015 additions and 1491 deletions

View file

@ -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()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/arcade/SHIN_YA.fur Normal file

Binary file not shown.

BIN
demos/arcade/Slow Brew.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/misc/david.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/opl/femteknyl.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/opz/Glacier BM0.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/opz/boost.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/opz/myst_grove.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/opz/sea of crises.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/pce/Peace.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/pce/fd_PCB.fur Normal file

Binary file not shown.

BIN
demos/pce/pcengineasdmo.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/vic20/h.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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);
}

View file

@ -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; i<REDUNDANCY_NUM_ATTEMPTS; i++) {
bool viable=false;
bool startCheck=true;
bool hasStartMarker=false;
unsigned char endMarker[18];
if (i>0) {
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<readBufLen; j++) {
if (readBuf[j]==0) {
viable=false;
willBreak=true;
logW("a zero?");
break;
}
@ -160,7 +182,30 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
}
}
if (viable) break;
if (readBufLen>=18) {
memcpy(endMarker,&readBuf[readBufLen-18],18);
} else if (readBufLen>0) {
// shift buffer left
for (size_t j=0, k=readBufLen; j<readBufLen && k<18; j++, k++) {
endMarker[j]=endMarker[k];
}
// copy to end
memcpy(&endMarker[18-readBufLen],readBuf,readBufLen);
}
if (willBreak) break;
}
// check for end marker if start marker is present
if (hasStartMarker) {
if (memcmp(endMarker,"\n~DIV_CONFIG_END~\n",18)!=0) {
// file is incomplete
viable=false;
logV("end marker NOT found!");
reportError("saved from an incomplete config.\nyeah! for a second I thought you were going to lose it.");
weRescued=true;
}
}
// there's something
@ -184,6 +229,9 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
logD("config does not exist");
if (createOnFail) {
logI("creating default config.");
if (weRescued) {
reportError("what the FUCK is that supposed to mean?!");
}
//reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
return save(path,redundancy);
} else {

View file

@ -31,6 +31,22 @@
#define DIV_MAX_COLS 32
#define DIV_MAX_EFFECTS 8
// pattern fields
#define DIV_PAT_NOTE 0
#define DIV_PAT_INS 1
#define DIV_PAT_VOL 2
#define DIV_PAT_FX(_x) (3+((_x)<<1))
#define DIV_PAT_FXVAL(_x) (4+((_x)<<1))
// column type checks
#define DIV_PAT_IS_EFFECT(_x) ((_x)>DIV_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

View file

@ -947,8 +947,8 @@ void DivEngine::delUnusedIns() {
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
for (int l=0; l<song.subsong[j]->patLen; 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; j<getTotalChannelCount(); j++) {
bool is17On=false;
int bank=0;
for (int k=0; k<i->ordersLen; k++) {
DivPattern* p=i->pat[j].getPattern(i->orders.ord[j][k],false);
for (int l=0; l<i->patLen; l++) {
for (int m=0; m<i->pat[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<song.sampleLen; i++) {
if (!isUsed[i]) {
@ -2293,6 +2261,64 @@ int DivEngine::getEffectiveSampleRate(int rate) {
return rate;
}
short DivEngine::splitNoteToNote(short note, short octave) {
if (note==100) {
return DIV_NOTE_OFF;
} else if (note==101) {
return DIV_NOTE_REL;
} else if (note==102) {
return DIV_MACRO_REL;
} else if (note==0 && octave!=0) {
// "BUG" note!
return DIV_NOTE_NULL_PAT;
} else if (note==0 && octave==0) {
return -1;
} else {
int seek=(note+(signed char)octave*12)+60;
if (seek<0 || seek>=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; k<DIV_MAX_PATTERNS; k++) {
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
for (int l=0; l<song.subsong[j]->patLen; 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; k<DIV_MAX_PATTERNS; k++) {
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
for (int l=0; l<song.subsong[j]->patLen; 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;
}
}
}

View file

@ -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);

View file

@ -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; k<ds.subsong[0]->patLen; 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; l<chan.effectCols; l++) {
// effect
pat->data[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]<ds.insLen) {
ds.ins[pat->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]<ds.insLen) {
ds.ins[pat->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]<ds.insLen) {
ds.ins[pat->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]<ds.insLen) {
ds.ins[pat->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; k<ds.subsong[0]->patLen; 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; i<getChannelCount(sys); i++) {
short note, octave;
w->writeC(curPat[i].effectCols);
for (int j=0; j<curSubSong->ordersLen; j++) {
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][j],false);
for (int k=0; k<curSubSong->patLen; 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; l<curPat[i].effectCols*2; l++) {
w->writeS(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
}
}
}

View file

@ -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];
}
}
}

View file

@ -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 && nextEffect<eftEffectMapSize) || (!eft && nextEffect<ftEffectMapSize)) {
if (eft) {
pat->data[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; l<i->patLen; 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; m<i->pat[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;
}
}
}

View file

@ -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<<k)) {
pat->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; j<ds.subsong[subs]->patLen; 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; k<ds.subsong[subs]->pat[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; k<ds.subsong[subs]->pat[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<int> subSongPtr;
std::vector<int> 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; j<song.subsong[i.subsong]->patLen; j++) {
unsigned char mask=0;
unsigned char finalNote=255;
unsigned short effectMask=0;
for (int j=0; j<song.subsong[i.subsong]->patLen; 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; k<song.subsong[i.subsong]->pat[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<<k);
if (pat->data[j][5+k]!=-1) effectMask|=(2<<k);
}
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->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; j<song.subsong[i.subsong]->patLen; 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; k<song.subsong[i.subsong]->pat[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; k<song.subsong[i.subsong]->pat[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<<k);
if (pat->newData[j][DIV_PAT_FXVAL(0)+k]!=-1) effectMask|=(2<<k);
}
w->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);

View file

@ -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; j<maxChan; j++) {
DivPattern* p=ds.subsong[i]->pat[j].getPattern(ds.subsong[i]->orders.ord[j][0],true);
for (int k=0; k<DIV_MAX_EFFECTS; k++) {
if (p->data[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;

View file

@ -185,21 +185,20 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
}
for (int row=0; row<64; row++) {
for (int ch=0; ch<chCount; ch++) {
short* dstrow=chpats[ch]->data[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]);
}
}

View file

@ -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; k<DIV_MAX_EFFECTS; k++) {
if (p->data[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;

View file

@ -321,9 +321,8 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
for (int l=0; l<chans; l++) {
DivPattern* p=s->pat[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; m<s->pat[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));
}
}
}

View file

@ -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]<info.ds->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]]<info.ds->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; k<truePatLen; k++) {
if (chArpeggio[j] && pat->data[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;
}
}
}

View file

@ -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; k<totalChans; k++) {
DivPattern* p=ds.subsong[0]->pat[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);

View file

@ -45,7 +45,7 @@ std::vector<std::pair<int,int>> DivChannelData::optimize() {
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
if (j==i) continue;
if (data[j]==NULL) continue;
if (memcmp(data[i]->data,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<DIV_MAX_ROWS; i++) {
data[i][0]=0;
data[i][1]=0;
}
memset(newData,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
}
DivChannelData::DivChannelData():

View file

@ -22,7 +22,22 @@
struct DivPattern {
String name;
short data[DIV_MAX_ROWS][DIV_MAX_COLS];
/**
* pattern data is stored in this order:
* - 0 note: 0 (min) is C-(-5), 60 is C-0, and 179 (max) is B-9.
252 is null/bug, 253 is note off, 254 is note release and
255 is macro release.
* - 1 instrument
* - 2 volume
* - 3 effect
* - 4 effect value
* - 5... (the rest of effects/effect values)
*
* use the DIV_PAT_* macros in defines.h for convenience.
*
* if a cell is -1, it means "empty".
*/
short newData[DIV_MAX_ROWS][DIV_MAX_COLS];
/**
* clear the pattern.
@ -39,14 +54,10 @@ struct DivPattern {
struct DivChannelData {
unsigned char effectCols;
// data goes as follows: data[ROW][TYPE]
// TYPE is:
// 0: note
// 1: octave
// 2: instrument
// 3: volume
// 4-5+: effect/effect value
// do NOT access directly unless you know what you're doing!
/**
* do NOT access directly unless you know what you're doing!
* use getPattern() instead.
*/
DivPattern* data[DIV_MAX_PATTERNS];
/**

File diff suppressed because it is too large Load diff

View file

@ -543,7 +543,11 @@ bool DivSample::saveRaw(const char* path) {
}
// 16-bit memory is padded to 512, to make things easier for ADPCM-A/B.
bool DivSample::initInternal(DivSampleDepth d, unsigned int count) {
bool DivSample::initInternal(DivSampleDepth d, int count) {
if (count<0) {
logE("initInternal(%d,%d) - NEGATIVE!",(int)d,count);
return false;
}
logV("initInternal(%d,%d)",(int)d,count);
switch (d) {
case DIV_SAMPLE_DEPTH_1BIT: // 1-bit
@ -650,8 +654,11 @@ bool DivSample::initInternal(DivSampleDepth d, unsigned int count) {
return true;
}
bool DivSample::init(unsigned int count) {
if (count>16777215) return false;
bool DivSample::init(int count) {
if (count<0 || count>16777215) {
logE("tried to init sample with length %d!",count);
return false;
}
if (!initInternal(depth,count)) return false;
setSampleCount(count);
return true;

View file

@ -236,14 +236,14 @@ struct DivSample {
* @param count number of samples.
* @return whether it was successful.
*/
bool initInternal(DivSampleDepth d, unsigned int count);
bool initInternal(DivSampleDepth d, int count);
/**
* initialize sample data. make sure you have set `depth` before doing so.
* @param count number of samples.
* @return whether it was successful.
*/
bool init(unsigned int count);
bool init(int count);
/**
* resize sample data. make sure the sample has been initialized before doing so.

View file

@ -54,9 +54,9 @@ bool DivSubSong::walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int
}
for (int k=0; k<chans; k++) {
for (int l=0; l<pat[k].effectCols; l++) {
effectVal=subPat[k]->data[j][5+(l<<1)];
effectVal=subPat[k]->newData[j][DIV_PAT_FXVAL(l)];
if (effectVal<0) effectVal=0;
if (subPat[k]->data[j][4+(l<<1)]==0x0d) {
if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0x0d) {
if (jumpTreatment==2) {
if ((i<ordersLen-1 || !ignoreJumpAtEnd)) {
nextOrder=i+1;
@ -78,7 +78,7 @@ bool DivSubSong::walk(int& loopOrder, int& loopRow, int& loopEnd, int chans, int
nextRow=effectVal;
}
}
} else if (subPat[k]->data[j][4+(l<<1)]==0x0b) {
} else if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0x0b) {
if (nextOrder==-1 || jumpTreatment==0) {
nextOrder=effectVal;
if (jumpTreatment==1 || jumpTreatment==2 || !jumpingOrder) {
@ -160,10 +160,10 @@ void DivSubSong::findLength(int loopOrder, int loopRow, double fadeoutLen, int&
}
for (int k=0; k<chans; k++) {
for (int l=0; l<pat[k].effectCols; l++) {
effectVal=subPat[k]->data[j][5+(l<<1)];
effectVal=subPat[k]->newData[j][DIV_PAT_FXVAL(l)];
if (effectVal<0) effectVal=0;
if (subPat[k]->data[j][4+(l<<1)]==0xff) {
if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0xff) {
hasFFxx=true;
// FFxx makes YOU SHALL NOT PASS!!! move
@ -173,7 +173,7 @@ void DivSubSong::findLength(int loopOrder, int loopRow, double fadeoutLen, int&
return;
}
switch (subPat[k]->data[j][4+(l<<1)]) {
switch (subPat[k]->newData[j][DIV_PAT_FX(l)]) {
case 0x09: { // select groove pattern/speed 1
if (grooves.empty()) {
if (effectVal>0) curSpeeds.val[0]=effectVal;
@ -208,7 +208,7 @@ void DivSubSong::findLength(int loopOrder, int loopRow, double fadeoutLen, int&
}
}
if (subPat[k]->data[j][4+(l<<1)]==0x0d) {
if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0x0d) {
if (jumpTreatment==2) {
if ((i<ordersLen-1 || !ignoreJumpAtEnd)) {
nextOrder=i+1;
@ -230,7 +230,7 @@ void DivSubSong::findLength(int loopOrder, int loopRow, double fadeoutLen, int&
nextRow=effectVal;
}
}
} else if (subPat[k]->data[j][4+(l<<1)]==0x0b) {
} else if (subPat[k]->newData[j][DIV_PAT_FX(l)]==0x0b) {
if (nextOrder==-1 || jumpTreatment==0) {
nextOrder=effectVal;
if (jumpTreatment==1 || jumpTreatment==2 || !jumpingOrder) {

View file

@ -902,10 +902,8 @@ void FurnaceGUI::drawChanOsc() {
case 'n': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL || !(chanState->keyOn)) break;
short tempNote=chanState->note; //all of this conversion is necessary because notes 100-102 are special chars
short noteMod=tempNote%12+12; //also note 0 is a BUG, hence +12 on the note and -1 on the octave
short oct=tempNote/12-1;
text+=fmt::sprintf("%s",noteName(noteMod,oct));
// no more conversion necessary after the note/octave unification :>
text+=fmt::sprintf("%s",noteName(chanState->note));
break;
}
case 'l': {

View file

@ -179,7 +179,6 @@ void FurnaceGUI::drawDebug() {
ImGui::TextColored(ch->portaStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> PortaStop");
ImGui::TextColored(ch->keyOn?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key On");
ImGui::TextColored(ch->keyOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key Off");
ImGui::TextColored(ch->nowYouCanStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> NowYouCanStop");
ImGui::TextColored(ch->stopOnOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Stop on Off");
ImGui::TextColored(ch->arpYield?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Arp Yield");
ImGui::TextColored(ch->delayLocked?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> DelayLocked");

View file

@ -697,10 +697,10 @@ void FurnaceGUI::doAction(int what) {
break;
case GUI_ACTION_PAT_LATCH: {
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][cursor.order],true);
latchIns=pat->data[cursor.y][2];
latchVol=pat->data[cursor.y][3];
latchEffect=pat->data[cursor.y][4];
latchEffectVal=pat->data[cursor.y][5];
latchIns=pat->newData[cursor.y][DIV_PAT_INS];
latchVol=pat->newData[cursor.y][DIV_PAT_VOL];
latchEffect=pat->newData[cursor.y][DIV_PAT_FX(0)];
latchEffectVal=pat->newData[cursor.y][DIV_PAT_FXVAL(0)];
latchTarget=0;
latchNibble=false;
break;

View file

@ -35,21 +35,22 @@ static const char* modPlugFormatHeaders[]={
NULL,
};
const char* FurnaceGUI::noteNameNormal(short note, short octave) {
if (note==100) { // note cut
const char* FurnaceGUI::noteNameNormal(short note) {
if (note==DIV_NOTE_OFF) { // note cut
return "OFF";
} else if (note==101) { // note off and envelope release
} else if (note==DIV_NOTE_REL) { // note off and envelope release
return "===";
} else if (note==102) { // envelope release only
} else if (note==DIV_MACRO_REL) { // envelope release only
return "REL";
} else if (octave==0 && note==0) {
} else if (note==-1) {
return "...";
} else if (note==DIV_NOTE_NULL_PAT) {
return "BUG";
}
int seek=(note+(signed char)octave*12)+60;
if (seek<0 || seek>=180) {
if (note<0 || note>=180) {
return "???";
}
return noteNames[seek];
return noteNames[note];
}
void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) {
@ -222,16 +223,16 @@ void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) {
for (int j=jBegin; j<=jEnd; j++) {
for (int k=0; k<DIV_MAX_COLS; k++) {
if (p->data[j][k]!=op->data[j][k]) {
s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][h],j,k,op->data[j][k],p->data[j][k]));
if (p->newData[j][k]!=op->newData[j][k]) {
s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][h],j,k,op->newData[j][k],p->newData[j][k]));
if (k>=4) {
if (op->data[j][k&(~1)]==0x0b ||
p->data[j][k&(~1)]==0x0b ||
op->data[j][k&(~1)]==0x0d ||
p->data[j][k&(~1)]==0x0d ||
op->data[j][k&(~1)]==0xff ||
p->data[j][k&(~1)]==0xff) {
if (k>=DIV_PAT_FX(0)) {
if (op->newData[j][k&(~1)]==0x0b ||
p->newData[j][k&(~1)]==0x0b ||
op->newData[j][k&(~1)]==0x0d ||
p->newData[j][k&(~1)]==0x0d ||
op->newData[j][k&(~1)]==0xff ||
p->newData[j][k&(~1)]==0xff) {
shallWalk=true;
}
}
@ -366,13 +367,12 @@ void FurnaceGUI::doDelete() {
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
touch(jOrder,j);
if (iFine==0) {
pat->data[j][iFine]=0;
if (selStart.y==selEnd.y && selStart.order==selEnd.order) pat->data[j][2]=-1;
if (selStart.y==selEnd.y && selStart.order==selEnd.order) pat->newData[j][DIV_PAT_VOL]=-1;
}
pat->data[j][iFine+1]=(iFine<1)?0:-1;
pat->newData[j][iFine]=-1;
if (selStart.y==selEnd.y && selStart.order==selEnd.order && iFine>2 && iFine&1 && settings.effectDeletionAltersValue) {
pat->data[j][iFine+2]=-1;
if (selStart.y==selEnd.y && selStart.order==selEnd.order && DIV_PAT_IS_EFFECT(iFine) && settings.effectDeletionAltersValue) {
pat->newData[j][iFine+1]=-1;
}
}
j=0;
@ -439,16 +439,12 @@ void FurnaceGUI::doPullDelete() {
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
maskOut(opMaskPullDelete,iFine);
for (int j=sStart.y; j<e->curSubSong->patLen; j++) {
// TODO: we've got a problem here. this should pull from the next row if the selection spans
// more than one order.
if (j<e->curSubSong->patLen-1) {
if (iFine==0) {
pat->data[j][iFine]=pat->data[j+1][iFine];
}
pat->data[j][iFine+1]=pat->data[j+1][iFine+1];
pat->newData[j][iFine]=pat->newData[j+1][iFine];
} else {
if (iFine==0) {
pat->data[j][iFine]=0;
}
pat->data[j][iFine+1]=(iFine<1)?0:-1;
pat->newData[j][iFine]=-1;
}
}
}
@ -486,15 +482,9 @@ void FurnaceGUI::doInsert() {
maskOut(opMaskInsert,iFine);
for (int j=e->curSubSong->patLen-1; j>=sStart.y; j--) {
if (j==sStart.y) {
if (iFine==0) {
pat->data[j][iFine]=0;
}
pat->data[j][iFine+1]=(iFine<1)?0:-1;
pat->newData[j][iFine]=-1;
} else {
if (iFine==0) {
pat->data[j][iFine]=pat->data[j-1][iFine];
}
pat->data[j][iFine+1]=pat->data[j-1][iFine+1];
pat->newData[j][iFine]=pat->newData[j-1][iFine];
}
}
}
@ -522,45 +512,22 @@ void FurnaceGUI::doTranspose(int amount, OperationMask& mask) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
touch(jOrder,j);
if (iFine==0) {
int origNote=pat->data[j][0];
int origOctave=(signed char)pat->data[j][1];
if (origNote!=0 && origNote!=100 && origNote!=101 && origNote!=102) {
origNote+=amount;
while (origNote>12) {
origNote-=12;
origOctave++;
}
while (origNote<1) {
origNote+=12;
origOctave--;
}
if (origOctave==9 && origNote>11) {
origNote=11;
origOctave=9;
}
if (origOctave>9) {
origNote=11;
origOctave=9;
}
if (origOctave<-5) {
origNote=1;
origOctave=-5;
}
pat->data[j][0]=origNote;
pat->data[j][1]=(unsigned char)origOctave;
}
} else {
int top=255;
if (iFine==1) {
if (e->song.ins.empty()) continue;
top=e->song.ins.size()-1;
} else if (iFine==2) { // volume
top=e->getMaxVolumeChan(iCoarse);
}
if (pat->data[j][iFine+1]==-1) continue;
pat->data[j][iFine+1]=MIN(top,MAX(0,pat->data[j][iFine+1]+amount));
int top=255;
if (iFine==DIV_PAT_NOTE) {
top=179;
// don't transpose special notes
if (pat->newData[j][iFine]==DIV_NOTE_OFF) continue;
if (pat->newData[j][iFine]==DIV_NOTE_REL) continue;
if (pat->newData[j][iFine]==DIV_MACRO_REL) continue;
if (pat->newData[j][iFine]==DIV_NOTE_NULL_PAT) continue;
} else if (iFine==DIV_PAT_INS) {
if (e->song.ins.empty()) continue;
top=e->song.ins.size()-1;
} else if (iFine==DIV_PAT_VOL) { // volume
top=e->getMaxVolumeChan(iCoarse);
}
if (pat->newData[j][iFine]==-1) continue;
pat->newData[j][iFine]=MIN(top,MAX(0,pat->newData[j][iFine]+amount));
}
j=0;
}
@ -596,19 +563,18 @@ String FurnaceGUI::doCopy(bool cut, bool writeClipboard, const SelectionPoint& s
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
if (iFine==0) {
clipb+=noteNameNormal(pat->data[j][0],pat->data[j][1]);
clipb+=noteNameNormal(pat->newData[j][DIV_PAT_NOTE]);
if (cut) {
pat->data[j][0]=0;
pat->data[j][1]=0;
pat->newData[j][DIV_PAT_NOTE]=-1;
}
} else {
if (pat->data[j][iFine+1]==-1) {
if (pat->newData[j][iFine]==-1) {
clipb+="..";
} else {
clipb+=fmt::sprintf("%.2X",pat->data[j][iFine+1]);
clipb+=fmt::sprintf("%.2X",pat->newData[j][iFine]);
}
if (cut) {
pat->data[j][iFine+1]=-1;
pat->newData[j][iFine]=-1;
}
}
}
@ -685,12 +651,12 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) && strcmp(note,"...")==0) {
// do nothing.
} else {
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->data[j][0]==0 && pat->data[j][1]==0)) {
if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) {
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->newData[j][DIV_PAT_NOTE]==-1)) {
if (!decodeNote(note,pat->newData[j][DIV_PAT_NOTE])) {
invalidData=true;
break;
}
if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->data[j][2]=arg;
if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->newData[j][DIV_PAT_INS]=arg;
}
}
} else {
@ -706,12 +672,12 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str
note[1]=line[charPos++];
note[2]=0;
if (iFine==1) {
if (iFine==DIV_PAT_INS) {
if (!opMaskPaste.ins || mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) {
iFine++;
continue;
}
} else if (iFine==2) {
} else if (iFine==DIV_PAT_VOL) {
if (!opMaskPaste.vol) {
iFine++;
continue;
@ -731,7 +697,7 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str
if (strcmp(note,"..")==0) {
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG ||
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) {
pat->data[j][iFine+1]=-1;
pat->newData[j][iFine]=-1;
}
} else {
unsigned int val=0;
@ -739,8 +705,8 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str
invalidData=true;
break;
}
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) {
if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val;
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->newData[j][iFine]==-1) {
if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->newData[j][iFine]=val;
}
}
}
@ -1024,7 +990,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
charPos++;
continue;
}
if (iFine==0) { // note
if (iFine==DIV_PAT_NOTE) { // note
if (charPos>=line.size()) {
invalidData=true;
break;
@ -1050,25 +1016,28 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
if (strcmp(note,"...")==0 || strcmp(note," ")==0) {
// do nothing.
} else {
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->data[j][0]==0 && pat->data[j][1]==0)) {
if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) {
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->newData[j][DIV_PAT_NOTE]==-1)) {
if (!decodeNote(note,pat->newData[j][DIV_PAT_NOTE])) {
if (strcmp(note, "^^^")==0) {
pat->data[j][0]=100;
pat->data[j][1]=0;
pat->newData[j][0]=DIV_NOTE_OFF;
} else if (strcmp(note, "~~~")==0 || strcmp(note,"===")==0) {
pat->data[j][0]=101;
pat->data[j][1]=0;
pat->newData[j][0]=DIV_NOTE_REL;
} else {
invalidData=true;
}
break;
} else {
pat->data[j][1]--; // MPT is one octave higher...
// MPT is one octave higher...
if (pat->newData[j][DIV_PAT_NOTE]<12) {
pat->newData[j][DIV_PAT_NOTE]=0;
} else {
pat->newData[j][DIV_PAT_NOTE]-=12;
}
}
if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->data[j][2]=arg;
if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->newData[j][DIV_PAT_INS]=arg;
}
}
} else if (iFine==1) { // instrument
} else if (iFine==DIV_PAT_INS) { // instrument
if (charPos>=line.size()) {
invalidData=true;
break;
@ -1081,7 +1050,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
note[1]=line[charPos++];
note[2]=0;
if (iFine==1) {
if (iFine==DIV_PAT_INS) {
if (!opMaskPaste.ins || mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) {
iFine++;
continue;
@ -1091,7 +1060,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
if (strcmp(note,"..")==0 || strcmp(note," ")==0) {
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG ||
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) {
pat->data[j][iFine+1]=-1;
pat->newData[j][iFine]=-1;
}
} else {
unsigned int val=0;
@ -1100,8 +1069,8 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
break;
}
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) {
pat->data[j][iFine+1]=val-1;
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->newData[j][iFine]==-1) {
pat->newData[j][iFine]=val-1;
}
}
} else { // volume and effects
@ -1122,14 +1091,12 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
note[2]=line[charPos++];
note[3]=0;
if (iFine==2) {
if (iFine==DIV_PAT_VOL) {
if (!opMaskPaste.vol) {
iFine++;
continue;
}
}
else if ((iFine&1)==0) {
} else if ((iFine&1)==0) { // FUCKING HELL WITH THE INDENTATION?!?!
if (!opMaskPaste.effectVal) {
iFine++;
continue;
@ -1143,9 +1110,8 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
if (strcmp(note,"...")==0 || strcmp(note," ")==0) {
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG ||
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG))
{
pat->data[j][iFine+1]=-1;
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) {
pat->newData[j][iFine]=-1;
}
} else {
unsigned int val=0;
@ -1153,19 +1119,19 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
symbol=note[0];
if (iFine==2) {
if (iFine==DIV_PAT_VOL) {
sscanf(&note[1],"%2d",&val);
} else {
sscanf(&note[1],"%2X",&val);
}
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) {
// if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val;
if (iFine==2) { // volume
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->newData[j][iFine]==-1) {
// if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->newData[j][iFine]=val;
if (iFine==DIV_PAT_VOL) { // volume
switch(symbol) {
case 'v':
{
pat->data[j][iFine+1]=val;
pat->newData[j][iFine]=val;
break;
}
default:
@ -1176,7 +1142,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
if (mptFormat==0) {
eff=convertEffectMPT_MOD(symbol, val); // up to 4 effects stored in one variable
if (((eff&0x0f00)>>8)==0x0C) { // set volume
pat->data[j][iFine]=eff&0xff;
pat->newData[j][iFine-1]=eff&0xff;
}
}
@ -1189,7 +1155,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
if (((eff&0x0f00)>>8)==0x0C)
{
pat->data[j][iFine]=eff&0xff;
pat->newData[j][iFine-1]=eff&0xff;
}
}
@ -1201,12 +1167,12 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
eff=convertEffectMPT_MPTM(symbol, val);
}
pat->data[j][iFine+1]=((eff&0xff00)>>8);
pat->data[j][iFine+2]=(eff&0xff);
pat->newData[j][iFine]=((eff&0xff00)>>8);
pat->newData[j][iFine+1]=(eff&0xff);
if (eff>0xffff) {
pat->data[j][iFine+3]=((eff&0xff000000)>>24);
pat->data[j][iFine+4]=((eff&0xff0000)>>16);
pat->newData[j][iFine+2]=((eff&0xff000000)>>24);
pat->newData[j][iFine+3]=((eff&0xff0000)>>16);
}
}
}
@ -1346,8 +1312,8 @@ void FurnaceGUI::doChangeIns(int ins) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
touch(jOrder,j);
if (pat->data[j][2]!=-1 || !((pat->data[j][0]==0 || pat->data[j][0]==100 || pat->data[j][0]==101 || pat->data[j][0]==102) && pat->data[j][1]==0)) {
pat->data[j][2]=ins;
if (pat->newData[j][DIV_PAT_INS]!=-1 || !(pat->newData[j][DIV_PAT_NOTE]==-1 || pat->newData[j][DIV_PAT_NOTE]==DIV_NOTE_OFF || pat->newData[j][DIV_PAT_NOTE]==DIV_NOTE_REL || pat->newData[j][DIV_PAT_NOTE]==DIV_MACRO_REL)) {
pat->newData[j][DIV_PAT_INS]=ins;
}
}
j=0;
@ -1372,15 +1338,15 @@ void FurnaceGUI::doInterpolate() {
maskOut(opMaskInterpolate,iFine);
points.clear();
resetTouches;
if (iFine!=0) {
if (iFine!=DIV_PAT_NOTE) {
int jOrder=selStart.order;
int j=selStart.y;
for (; jOrder<=selEnd.order; jOrder++) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
touch(jOrder,j);
if (pat->data[j][iFine+1]!=-1) {
points.emplace(points.end(),j|(jOrder<<8),pat->data[j][iFine+1]);
if (pat->newData[j][iFine]!=-1) {
points.emplace(points.end(),j|(jOrder<<8),pat->newData[j][iFine]);
}
}
j=0;
@ -1395,7 +1361,7 @@ void FurnaceGUI::doInterpolate() {
);
for (int k=0, k_p=curPoint.first; k<distance; k++) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][(k_p>>8)&0xff],true);
pat->data[k_p&0xff][iFine+1]=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/(double)distance);
pat->newData[k_p&0xff][iFine]=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/(double)distance);
k_p++;
if ((k_p&0xff)>=e->curSubSong->patLen) {
k_p&=~0xff;
@ -1410,9 +1376,9 @@ void FurnaceGUI::doInterpolate() {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
touch(jOrder,j);
if (pat->data[j][0]!=0 || pat->data[j][1]!=0) {
if (pat->data[j][0]!=100 && pat->data[j][0]!=101 && pat->data[j][0]!=102) {
points.emplace(points.end(),j|(jOrder<<8),pat->data[j][0]+(signed char)pat->data[j][1]*12);
if (pat->newData[j][DIV_PAT_NOTE]!=-1) {
if (pat->newData[j][DIV_PAT_NOTE]!=DIV_NOTE_OFF && pat->newData[j][DIV_PAT_NOTE]!=DIV_NOTE_REL && pat->newData[j][DIV_PAT_NOTE]!=DIV_MACRO_REL) {
points.emplace(points.end(),j|(jOrder<<8),pat->newData[j][DIV_PAT_NOTE]);
}
}
}
@ -1429,13 +1395,7 @@ void FurnaceGUI::doInterpolate() {
for (int k=0, k_p=curPoint.first; k<distance; k++) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][(k_p>>8)&0xff],true);
int val=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/(double)distance);
pat->data[k_p&0xff][0]=val%12;
pat->data[k_p&0xff][1]=val/12;
if (pat->data[k_p&0xff][0]==0) {
pat->data[k_p&0xff][0]=12;
pat->data[k_p&0xff][1]--;
}
pat->data[k_p&0xff][1]&=255;
pat->newData[k_p&0xff][DIV_PAT_NOTE]=val%12;
k_p++;
if ((k_p&0xff)>=e->curSubSong->patLen) {
k_p&=~0xff;
@ -1469,12 +1429,12 @@ void FurnaceGUI::doFade(int p0, int p1, bool mode) {
int j_p=0;
maskOut(opMaskFade,iFine);
resetTouches;
if (iFine!=0) {
if (iFine!=DIV_PAT_NOTE) {
int absoluteTop=255;
if (iFine==1) {
if (iFine==DIV_PAT_INS) {
if (e->song.ins.empty()) continue;
absoluteTop=e->song.ins.size()-1;
} else if (iFine==2) { // volume
} else if (iFine==DIV_PAT_VOL) { // volume
absoluteTop=e->getMaxVolumeChan(iCoarse);
}
if (distance<1) continue;
@ -1485,9 +1445,9 @@ void FurnaceGUI::doFade(int p0, int p1, bool mode) {
int value=p0+double(p1-p0)*fraction;
if (mode) { // nibble
value&=15;
pat->data[j][iFine+1]=MIN(absoluteTop,value|(value<<4));
pat->newData[j][iFine]=MIN(absoluteTop,value|(value<<4));
} else { // byte
pat->data[j][iFine+1]=MIN(absoluteTop,value);
pat->newData[j][iFine]=MIN(absoluteTop,value);
}
j_p++;
}
@ -1514,20 +1474,20 @@ void FurnaceGUI::doInvertValues() {
int j=selStart.y;
maskOut(opMaskInvertVal,iFine);
resetTouches;
if (iFine!=0) {
if (iFine!=DIV_PAT_NOTE) {
int top=255;
if (iFine==1) {
if (iFine==DIV_PAT_INS) {
if (e->song.ins.empty()) continue;
top=e->song.ins.size()-1;
} else if (iFine==2) { // volume
} else if (iFine==DIV_PAT_VOL) { // volume
top=e->getMaxVolumeChan(iCoarse);
}
for (; jOrder<=selEnd.order; jOrder++) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
touch(jOrder,j);
if (pat->data[j][iFine+1]==-1) continue;
pat->data[j][iFine+1]=top-pat->data[j][iFine+1];
if (pat->newData[j][iFine]==-1) continue;
pat->newData[j][iFine]=top-pat->newData[j][iFine];
}
j=0;
}
@ -1552,20 +1512,20 @@ void FurnaceGUI::doScale(float top) {
int j=selStart.y;
maskOut(opMaskScale,iFine);
resetTouches;
if (iFine!=0) {
if (iFine!=DIV_PAT_NOTE) {
int absoluteTop=255;
if (iFine==1) {
if (iFine==DIV_PAT_INS) {
if (e->song.ins.empty()) continue;
absoluteTop=e->song.ins.size()-1;
} else if (iFine==2) { // volume
} else if (iFine==DIV_PAT_VOL) { // volume
absoluteTop=e->getMaxVolumeChan(iCoarse);
}
for (; jOrder<=selEnd.order; jOrder++) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
touch(jOrder,j);
if (pat->data[j][iFine+1]==-1) continue;
pat->data[j][iFine+1]=MIN(absoluteTop,(double)pat->data[j][iFine+1]*(top/100.0f));
if (pat->newData[j][iFine]==-1) continue;
pat->newData[j][iFine]=MIN(absoluteTop,(double)pat->newData[j][iFine]*(top/100.0f));
}
j=0;
}
@ -1590,63 +1550,40 @@ void FurnaceGUI::doRandomize(int bottom, int top, bool mode, bool eff, int effVa
int j=selStart.y;
maskOut(opMaskRandomize,iFine);
resetTouches;
if (iFine!=0) {
int absoluteTop=255;
if (iFine==1) {
if (e->song.ins.empty()) continue;
absoluteTop=e->song.ins.size()-1;
} else if (iFine==2) { // volume
absoluteTop=e->getMaxVolumeChan(iCoarse);
}
for (; jOrder<=selEnd.order; jOrder++) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
int value=0;
int value2=0;
touch(jOrder,j);
if (top-bottom<=0) {
value=MIN(absoluteTop,bottom);
value2=MIN(absoluteTop,bottom);
} else {
value=MIN(absoluteTop,bottom+(rand()%(top-bottom+1)));
value2=MIN(absoluteTop,bottom+(rand()%(top-bottom+1)));
}
if (mode) {
value&=15;
value2&=15;
pat->data[j][iFine+1]=value|(value2<<4);
} else {
pat->data[j][iFine+1]=value;
}
if (eff && iFine>2 && (iFine&1)) {
pat->data[j][iFine+1]=effVal;
}
int absoluteTop=255;
if (iFine==DIV_PAT_NOTE) {
absoluteTop=179;
} else if (iFine==DIV_PAT_INS) {
if (e->song.ins.empty()) continue;
absoluteTop=e->song.ins.size()-1;
} else if (iFine==DIV_PAT_VOL) { // volume
absoluteTop=e->getMaxVolumeChan(iCoarse);
}
for (; jOrder<=selEnd.order; jOrder++) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
int value=0;
int value2=0;
touch(jOrder,j);
if (top-bottom<=0) {
value=MIN(absoluteTop,bottom);
value2=MIN(absoluteTop,bottom);
} else {
value=MIN(absoluteTop,bottom+(rand()%(top-bottom+1)));
value2=MIN(absoluteTop,bottom+(rand()%(top-bottom+1)));
}
j=0;
}
} else {
// random notes
int absoluteTop=179;
for (; jOrder<=selEnd.order; jOrder++) {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
int value=0;
touch(jOrder,j);
if (top-bottom<=0) {
value=MIN(absoluteTop,bottom);
} else {
value=MIN(absoluteTop,bottom+(rand()%(top-bottom+1)));
}
pat->data[j][0]=value%12;
pat->data[j][1]=(value-60)/12;
if (pat->data[j][0]==0) {
pat->data[j][0]=12;
pat->data[j][1]--;
}
pat->data[j][1]=(unsigned char)pat->data[j][1];
if (mode) {
value&=15;
value2&=15;
pat->newData[j][iFine]=value|(value2<<4);
} else {
pat->newData[j][iFine]=value;
}
if (eff && iFine>2 && (iFine&1)) {
pat->newData[j][iFine]=effVal;
}
j=0;
}
j=0;
}
}
iFine=0;
@ -1678,7 +1615,7 @@ void FurnaceGUI::doFlip() {
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][jOrder],true);
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
PatBufferEntry put;
memcpy(put.data,pat->data[j],DIV_MAX_COLS*sizeof(short));
memcpy(put.data,pat->newData[j],DIV_MAX_COLS*sizeof(short));
patBuffer.push_back(put);
}
j=0;
@ -1697,10 +1634,7 @@ void FurnaceGUI::doFlip() {
for (; j<e->curSubSong->patLen && (j<=selEnd.y || jOrder<selEnd.order); j++) {
j_i--;
touch(jOrder,j);
if (iFine==0) {
pat->data[j][0]=patBuffer[j_i].data[0];
}
pat->data[j][iFine+1]=patBuffer[j_i].data[iFine+1];
pat->newData[j][iFine]=patBuffer[j_i].data[iFine];
}
j=0;
}
@ -1734,38 +1668,18 @@ void FurnaceGUI::doCollapse(int divider, const SelectionPoint& sStart, const Sel
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
maskOut(opMaskCollapseExpand,iFine);
for (int j=sStart.y; j<=sEnd.y; j++) {
if (iFine==0) {
patBuffer.data[j][0]=pat->data[j][0];
}
patBuffer.data[j][iFine+1]=pat->data[j][iFine+1];
patBuffer.newData[j][iFine]=pat->newData[j][iFine];
}
for (int j=0; j<=sEnd.y-sStart.y; j++) {
if (j*divider>=sEnd.y-sStart.y) {
if (iFine==0) {
pat->data[j+sStart.y][0]=0;
pat->data[j+sStart.y][1]=0;
} else {
pat->data[j+sStart.y][iFine+1]=-1;
}
pat->newData[j+sStart.y][iFine]=-1;
} else {
if (iFine==0) {
pat->data[j+sStart.y][0]=patBuffer.data[j*divider+sStart.y][0];
}
pat->data[j+sStart.y][iFine+1]=patBuffer.data[j*divider+sStart.y][iFine+1];
pat->newData[j+sStart.y][iFine+1]=patBuffer.newData[j*divider+sStart.y][iFine];
if (iFine==0) {
for (int k=1; k<divider; k++) {
if ((j*divider+k)>=sEnd.y-sStart.y) break;
if (!(pat->data[j+sStart.y][0]==0 && pat->data[j+sStart.y][1]==0)) break;
pat->data[j+sStart.y][0]=patBuffer.data[j*divider+sStart.y+k][0];
pat->data[j+sStart.y][1]=patBuffer.data[j*divider+sStart.y+k][1];
}
} else {
for (int k=1; k<divider; k++) {
if ((j*divider+k)>=sEnd.y-sStart.y) break;
if (pat->data[j+sStart.y][iFine+1]!=-1) break;
pat->data[j+sStart.y][iFine+1]=patBuffer.data[j*divider+sStart.y+k][iFine+1];
}
for (int k=1; k<divider; k++) {
if ((j*divider+k)>=sEnd.y-sStart.y) break;
if (pat->newData[j+sStart.y][iFine]!=-1) break;
pat->newData[j+sStart.y][iFine]=patBuffer.newData[j*divider+sStart.y+k][iFine];
}
}
}
@ -1795,26 +1709,15 @@ void FurnaceGUI::doExpand(int multiplier, const SelectionPoint& sStart, const Se
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
maskOut(opMaskCollapseExpand,iFine);
for (int j=sStart.y; j<=sEnd.y; j++) {
if (iFine==0) {
patBuffer.data[j][0]=pat->data[j][0];
}
patBuffer.data[j][iFine+1]=pat->data[j][iFine+1];
patBuffer.newData[j][iFine]=pat->newData[j][iFine];
}
for (int j=0; j<=(sEnd.y-sStart.y)*multiplier; j++) {
if ((j+sStart.y)>=e->curSubSong->patLen) break;
if ((j%multiplier)!=0) {
if (iFine==0) {
pat->data[j+sStart.y][0]=0;
pat->data[j+sStart.y][1]=0;
} else {
pat->data[j+sStart.y][iFine+1]=-1;
}
pat->newData[j+sStart.y][iFine]=-1;
continue;
}
if (iFine==0) {
pat->data[j+sStart.y][0]=patBuffer.data[j/multiplier+sStart.y][0];
}
pat->data[j+sStart.y][iFine+1]=patBuffer.data[j/multiplier+sStart.y][iFine+1];
pat->newData[j+sStart.y][iFine]=patBuffer.newData[j/multiplier+sStart.y][iFine];
}
}
iFine=0;
@ -1846,24 +1749,17 @@ void FurnaceGUI::doCollapseSong(int divider) {
pat->clear();
for (int k=0; k<DIV_MAX_ROWS; k++) {
for (int l=0; l<DIV_MAX_COLS; l++) {
if (l==0) {
if (!(pat->data[k/divider][0]==0 && pat->data[k/divider][1]==0)) continue;
} else {
if (pat->data[k/divider][l+1]!=-1) continue;
}
if (pat->newData[k/divider][l]!=-1) continue;
if (l==0) {
pat->data[k/divider][l]=patCopy.data[k][l];
}
pat->data[k/divider][l+1]=patCopy.data[k][l+1];
pat->newData[k/divider][l]=patCopy.newData[k][l];
if (l>3 && !(l&1)) { // scale effects as needed
switch (pat->data[k/divider][l]) {
if (DIV_PAT_IS_EFFECT(l)) { // scale effects as needed
switch (pat->newData[k/divider][l]) {
case 0x0d:
pat->data[k/divider][l+1]/=divider;
pat->newData[k/divider][l+1]/=divider;
break;
case 0x0f:
pat->data[k/divider][l+1]=CLAMP(pat->data[k/divider][l+1]*divider,1,255);
pat->newData[k/divider][l+1]=CLAMP(pat->newData[k/divider][l+1]*divider,1,255);
break;
}
}
@ -1873,8 +1769,8 @@ void FurnaceGUI::doCollapseSong(int divider) {
// put undo
for (int k=0; k<DIV_MAX_ROWS; k++) {
for (int l=0; l<DIV_MAX_COLS; l++) {
if (pat->data[k][l]!=patCopy.data[k][l]) {
us.pat.push_back(UndoPatternData(subSong,i,j,k,l,patCopy.data[k][l],pat->data[k][l]));
if (pat->newData[k][l]!=patCopy.newData[k][l]) {
us.pat.push_back(UndoPatternData(subSong,i,j,k,l,patCopy.newData[k][l],pat->newData[k][l]));
}
}
}
@ -1929,24 +1825,17 @@ void FurnaceGUI::doExpandSong(int multiplier) {
pat->clear();
for (int k=0; k<(256/multiplier); k++) {
for (int l=0; l<DIV_MAX_COLS; l++) {
if (l==0) {
if (!(pat->data[k*multiplier][0]==0 && pat->data[k*multiplier][1]==0)) continue;
} else {
if (pat->data[k*multiplier][l+1]!=-1) continue;
}
if (pat->newData[k*multiplier][l]!=-1) continue;
if (l==0) {
pat->data[k*multiplier][l]=patCopy.data[k][l];
}
pat->data[k*multiplier][l+1]=patCopy.data[k][l+1];
pat->newData[k*multiplier][l]=patCopy.newData[k][l];
if (l>3 && !(l&1)) { // scale effects as needed
switch (pat->data[k*multiplier][l]) {
if (DIV_PAT_IS_EFFECT(l)) { // scale effects as needed
switch (pat->newData[k*multiplier][l]) {
case 0x0d:
pat->data[k*multiplier][l+1]/=multiplier;
pat->newData[k*multiplier][l+1]/=multiplier;
break;
case 0x0f:
pat->data[k*multiplier][l+1]=CLAMP(pat->data[k*multiplier][l+1]/multiplier,1,255);
pat->newData[k*multiplier][l+1]=CLAMP(pat->newData[k*multiplier][l+1]/multiplier,1,255);
break;
}
}
@ -1956,8 +1845,8 @@ void FurnaceGUI::doExpandSong(int multiplier) {
// put undo
for (int k=0; k<DIV_MAX_ROWS; k++) {
for (int l=0; l<DIV_MAX_COLS; l++) {
if (pat->data[k][l]!=patCopy.data[k][l]) {
us.pat.push_back(UndoPatternData(subSong,i,j,k,l,patCopy.data[k][l],pat->data[k][l]));
if (pat->newData[k][l]!=patCopy.newData[k][l]) {
us.pat.push_back(UndoPatternData(subSong,i,j,k,l,patCopy.newData[k][l],pat->newData[k][l]));
}
}
}
@ -2006,28 +1895,23 @@ void FurnaceGUI::doAbsorbInstrument() {
for (int i=searchStartRow; i>=0 && !foundAll(); i--) {
// absorb most recent instrument
if (!foundIns && pat->data[i][2] >= 0) {
if (!foundIns && pat->newData[i][DIV_PAT_INS] >= 0) {
foundIns=true;
curIns=pat->data[i][2];
curIns=pat->newData[i][DIV_PAT_INS];
}
// absorb most recent octave (i.e. set curOctave such that the "main row" (QWERTY) of
// notes will result in an octave number equal to the previous note). make sure to
// skip "special note values" like OFF/REL/=== and "none", since there won't be valid
// octave values
unsigned char note=pat->data[i][0];
if (!foundOctave && note!=0 && note!=100 && note!=101 && note!=102) {
short note=pat->newData[i][DIV_PAT_NOTE];
if (!foundOctave && note!=-1 && note!=DIV_NOTE_OFF && note!=DIV_NOTE_REL && note!=DIV_MACRO_REL) {
foundOctave=true;
// decode octave data (was signed cast to unsigned char)
int octave=pat->data[i][1];
if (octave>128) octave-=256;
// decode octave data
int octave=(pat->newData[i][DIV_PAT_NOTE]-60)/12;
// @NOTE the special handling when note==12, which is really an octave above what's
// stored in the octave data. without this handling, if you press Q, then
// "ABSORB_INSTRUMENT", then Q again, you'd get a different octave!
if (pat->data[i][0]==12) octave++;
curOctave=CLAMP(octave-1, GUI_EDIT_OCTAVE_MIN, GUI_EDIT_OCTAVE_MAX);
curOctave=CLAMP(octave-1,GUI_EDIT_OCTAVE_MIN,GUI_EDIT_OCTAVE_MAX);
}
}
}
@ -2221,7 +2105,7 @@ void FurnaceGUI::doUndo() {
for (UndoPatternData& i: us.pat) {
e->changeSongP(i.subSong);
DivPattern* p=e->curPat[i.chan].getPattern(i.pat,true);
p->data[i.row][i.col]=i.oldVal;
p->newData[i.row][i.col]=i.oldVal;
}
if (us.type!=GUI_UNDO_REPLACE) {
if (!e->isPlaying() || !followPattern) {
@ -2299,7 +2183,7 @@ void FurnaceGUI::doRedo() {
for (UndoPatternData& i: us.pat) {
e->changeSongP(i.subSong);
DivPattern* p=e->curPat[i.chan].getPattern(i.pat,true);
p->data[i.row][i.col]=i.newVal;
p->newData[i.row][i.col]=i.newVal;
}
if (us.type!=GUI_UNDO_REPLACE) {
if (!e->isPlaying() || !followPattern) {

View file

@ -43,25 +43,6 @@ const char* queryReplaceModes[GUI_QUERY_REPLACE_MAX]={
_N("clear")
};
int queryNote(int note, int octave) {
if (note==100) {
return 128;
} else if (note==101) { // note off and envelope release
return 129;
} else if (note==102) { // envelope release only
return 130;
} else if (octave==0 && note==0) {
return -61;
} else if (note==0 && octave!=0) {
return -61; // bug note?
}
int seek=(note+(signed char)octave*12);
if (seek<-60 || seek>=120) {
return -61; // out of range note
}
return seek;
}
bool checkCondition(int mode, int arg, int argMax, int val, bool noteMode=false) {
const int emptyVal=noteMode?-61:-1;
switch (mode) {
@ -137,9 +118,9 @@ void FurnaceGUI::doFind() {
for (FurnaceGUIFindQuery& l: curQuery) {
if (matched) break;
if (!checkCondition(l.noteMode,l.note,l.noteMax,queryNote(p->data[j][0],p->data[j][1]),true)) continue;
if (!checkCondition(l.insMode,l.ins,l.insMax,p->data[j][2])) continue;
if (!checkCondition(l.volMode,l.vol,l.volMax,p->data[j][3])) continue;
if (!checkCondition(l.noteMode,l.note,l.noteMax,p->newData[j][DIV_PAT_NOTE],true)) continue;
if (!checkCondition(l.insMode,l.ins,l.insMax,p->newData[j][DIV_PAT_INS])) continue;
if (!checkCondition(l.volMode,l.vol,l.volMax,p->newData[j][DIV_PAT_VOL])) continue;
if (l.effectCount>0) {
bool notMatched=false;
@ -148,8 +129,8 @@ void FurnaceGUI::doFind() {
for (int m=0; m<l.effectCount; m++) {
bool allGood=false;
for (int n=0; n<e->curPat[k].effectCols; n++) {
if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->data[j][4+n*2])) continue;
if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->data[j][5+n*2])) continue;
if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->newData[j][DIV_PAT_FX(n)])) continue;
if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->newData[j][DIV_PAT_FXVAL(n)])) continue;
allGood=true;
effectPos[m]=n;
break;
@ -164,8 +145,8 @@ void FurnaceGUI::doFind() {
// locate first effect
int posOfFirst=-1;
for (int m=0; m<e->curPat[k].effectCols; m++) {
if (!checkCondition(l.effectMode[0],l.effect[0],l.effectMax[0],p->data[j][4+m*2])) continue;
if (!checkCondition(l.effectValMode[0],l.effectVal[0],l.effectValMax[0],p->data[j][5+m*2])) continue;
if (!checkCondition(l.effectMode[0],l.effect[0],l.effectMax[0],p->newData[j][DIV_PAT_FX(m)])) continue;
if (!checkCondition(l.effectValMode[0],l.effectVal[0],l.effectValMax[0],p->newData[j][DIV_PAT_FXVAL(m)])) continue;
posOfFirst=m;
break;
}
@ -180,11 +161,11 @@ void FurnaceGUI::doFind() {
}
// search from first effect location
for (int m=0; m<l.effectCount; m++) {
if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->data[j][4+(m+posOfFirst)*2])) {
if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->newData[j][DIV_PAT_FX(m+posOfFirst)])) {
notMatched=true;
break;
}
if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->data[j][5+(m+posOfFirst)*2])) {
if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->newData[j][DIV_PAT_FXVAL(m+posOfFirst)])) {
notMatched=true;
break;
}
@ -198,11 +179,11 @@ void FurnaceGUI::doFind() {
notMatched=true;
} else {
for (int m=0; m<effectMax; m++) {
if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->data[j][4+m*2])) {
if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->newData[j][DIV_PAT_FX(m)])) {
notMatched=true;
break;
}
if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->data[j][5+m*2])) {
if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->newData[j][DIV_PAT_FXVAL(m)])) {
notMatched=true;
break;
}
@ -248,131 +229,107 @@ void FurnaceGUI::doReplace() {
if (touched[i.x][(patIndex<<8)|i.y]) continue;
touched[i.x][(patIndex<<8)|i.y]=true;
memcpy(prevVal,p->data[i.y],DIV_MAX_COLS*sizeof(short));
memcpy(prevVal,p->newData[i.y],DIV_MAX_COLS*sizeof(short));
// replace note
if (queryReplaceNoteDo) {
switch (queryReplaceNoteMode) {
case GUI_QUERY_REPLACE_SET:
if (queryReplaceNote==130) { // macro release
p->data[i.y][0]=102;
p->data[i.y][1]=0;
} else if (queryReplaceNote==129) { // note release
p->data[i.y][0]=101;
p->data[i.y][1]=0;
} else if (queryReplaceNote==128) { // note off
p->data[i.y][0]=100;
p->data[i.y][1]=0;
} else if (queryReplaceNote>=-60 && queryReplaceNote<120) { // note
p->data[i.y][0]=(queryReplaceNote+60)%12;
if (p->data[i.y][0]==0) p->data[i.y][0]=12;
p->data[i.y][1]=(unsigned char)((queryReplaceNote-1)/12);
} else { // invalid
p->data[i.y][0]=0;
p->data[i.y][1]=0;
}
p->newData[i.y][DIV_PAT_NOTE]=queryReplaceNote;
break;
case GUI_QUERY_REPLACE_ADD:
if (p->data[i.y][0]<100) {
int note=queryNote(p->data[i.y][0],p->data[i.y][1]);
if (note>=-60 && note<120) {
note+=queryReplaceNote;
if (note<-60) note=-60;
if (note>119) note=119;
case GUI_QUERY_REPLACE_ADD: {
int note=p->newData[i.y][DIV_PAT_NOTE];
if (note>=0 && note<180) {
note+=queryReplaceNote;
if (note<0) note=0;
if (note>179) note=179;
p->data[i.y][0]=(note+60)%12;
p->data[i.y][1]=(unsigned char)(((note+60)/12)-5);
if (p->data[i.y][0]==0) {
p->data[i.y][0]=12;
p->data[i.y][1]=(unsigned char)(p->data[i.y][1]-1);
}
}
p->newData[i.y][DIV_PAT_NOTE]=note;
}
break;
case GUI_QUERY_REPLACE_ADD_OVERFLOW:
if (p->data[i.y][0]<100) {
int note=queryNote(p->data[i.y][0],p->data[i.y][1]);
if (note>=-60 && note<120) {
}
case GUI_QUERY_REPLACE_ADD_OVERFLOW: {
int note=p->newData[i.y][DIV_PAT_NOTE];
if (note>=0 && note<180) {
note+=queryReplaceNote;
if (note<-60) {
while (note<-60) note+=180;
} else if (note>119) {
while (note>119) note-=180;
if (note<0) {
while (note<0) note+=180;
} else if (note>179) {
while (note>179) note-=180;
}
p->data[i.y][0]=(note+60)%12;
p->data[i.y][1]=(unsigned char)(((note+60)/12)-5);
if (p->data[i.y][0]==0) {
p->data[i.y][0]=12;
p->data[i.y][1]=(unsigned char)(p->data[i.y][1]-1);
}
p->newData[i.y][DIV_PAT_NOTE]=note;
}
}
break;
}
case GUI_QUERY_REPLACE_SCALE:
break;
case GUI_QUERY_REPLACE_CLEAR:
p->data[i.y][0]=0;
p->data[i.y][1]=0;
p->newData[i.y][DIV_PAT_NOTE]=-1;
break;
}
}
// replace ins
if (queryReplaceInsDo) {
switch (queryReplaceInsMode) {
case GUI_QUERY_REPLACE_SET:
p->data[i.y][2]=queryReplaceIns;
p->newData[i.y][DIV_PAT_INS]=queryReplaceIns;
break;
case GUI_QUERY_REPLACE_ADD:
if (p->data[i.y][2]>=0) {
p->data[i.y][2]+=queryReplaceIns;
if (p->data[i.y][2]<0) p->data[i.y][2]=0;
if (p->data[i.y][2]>255) p->data[i.y][2]=255;
if (p->newData[i.y][DIV_PAT_INS]>=0) {
p->newData[i.y][DIV_PAT_INS]+=queryReplaceIns;
if (p->newData[i.y][DIV_PAT_INS]<0) p->newData[i.y][DIV_PAT_INS]=0;
if (p->newData[i.y][DIV_PAT_INS]>255) p->newData[i.y][DIV_PAT_INS]=255;
}
break;
case GUI_QUERY_REPLACE_ADD_OVERFLOW:
if (p->data[i.y][2]>=0) p->data[i.y][2]=(p->data[i.y][2]+queryReplaceIns)&0xff;
if (p->newData[i.y][DIV_PAT_INS]>=0) p->newData[i.y][DIV_PAT_INS]=(p->newData[i.y][DIV_PAT_INS]+queryReplaceIns)&0xff;
break;
case GUI_QUERY_REPLACE_SCALE:
if (p->data[i.y][2]>=0) {
p->data[i.y][2]=(p->data[i.y][2]*queryReplaceIns)/100;
if (p->data[i.y][2]<0) p->data[i.y][2]=0;
if (p->data[i.y][2]>255) p->data[i.y][2]=255;
if (p->newData[i.y][DIV_PAT_INS]>=0) {
p->newData[i.y][DIV_PAT_INS]=(p->newData[i.y][DIV_PAT_INS]*queryReplaceIns)/100;
if (p->newData[i.y][DIV_PAT_INS]<0) p->newData[i.y][DIV_PAT_INS]=0;
if (p->newData[i.y][DIV_PAT_INS]>255) p->newData[i.y][DIV_PAT_INS]=255;
}
break;
case GUI_QUERY_REPLACE_CLEAR:
p->data[i.y][2]=-1;
p->newData[i.y][DIV_PAT_INS]=-1;
break;
}
}
// replace vol
if (queryReplaceVolDo) {
switch (queryReplaceVolMode) {
case GUI_QUERY_REPLACE_SET:
p->data[i.y][3]=queryReplaceVol;
p->newData[i.y][DIV_PAT_VOL]=queryReplaceVol;
break;
case GUI_QUERY_REPLACE_ADD:
if (p->data[i.y][3]>=0) {
p->data[i.y][3]+=queryReplaceVol;
if (p->data[i.y][3]<0) p->data[i.y][3]=0;
if (p->data[i.y][3]>255) p->data[i.y][3]=255;
if (p->newData[i.y][DIV_PAT_VOL]>=0) {
p->newData[i.y][DIV_PAT_VOL]+=queryReplaceVol;
if (p->newData[i.y][DIV_PAT_VOL]<0) p->newData[i.y][DIV_PAT_VOL]=0;
if (p->newData[i.y][DIV_PAT_VOL]>255) p->newData[i.y][DIV_PAT_VOL]=255;
}
break;
case GUI_QUERY_REPLACE_ADD_OVERFLOW:
if (p->data[i.y][3]>=0) p->data[i.y][3]=(p->data[i.y][3]+queryReplaceVol)&0xff;
if (p->newData[i.y][DIV_PAT_VOL]>=0) p->newData[i.y][DIV_PAT_VOL]=(p->newData[i.y][DIV_PAT_VOL]+queryReplaceVol)&0xff;
break;
case GUI_QUERY_REPLACE_SCALE:
if (p->data[i.y][3]>=0) {
p->data[i.y][3]=(p->data[i.y][3]*queryReplaceVol)/100;
if (p->data[i.y][3]<0) p->data[i.y][3]=0;
if (p->data[i.y][3]>255) p->data[i.y][3]=255;
if (p->newData[i.y][DIV_PAT_VOL]>=0) {
p->newData[i.y][DIV_PAT_VOL]=(p->newData[i.y][DIV_PAT_VOL]*queryReplaceVol)/100;
if (p->newData[i.y][DIV_PAT_VOL]<0) p->newData[i.y][DIV_PAT_VOL]=0;
if (p->newData[i.y][DIV_PAT_VOL]>255) p->newData[i.y][DIV_PAT_VOL]=255;
}
break;
case GUI_QUERY_REPLACE_CLEAR:
p->data[i.y][3]=-1;
p->newData[i.y][DIV_PAT_VOL]=-1;
break;
}
}
// effect replacement is a bit more complicated
// first we consider effect replacement position
signed char effectOrder[8];
memset(effectOrder,-1,8);
@ -388,7 +345,7 @@ void FurnaceGUI::doReplace() {
effectOrder[placementIndex++]=i.effectPos[j];
}
for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols && placementIndex<8 && j<8; j++) {
if (p->data[i.y][4+j*2]!=-1 || p->data[i.y][5+j*2]!=-1) {
if (p->newData[i.y][DIV_PAT_FX(j)]!=-1 || p->newData[i.y][DIV_PAT_FXVAL(j)]!=-1) {
effectOrder[placementIndex++]=j;
}
}
@ -400,12 +357,12 @@ void FurnaceGUI::doReplace() {
effectOrder[placementIndex++]=i.effectPos[j];
}
for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols && placementIndex<8 && j<8; j++) {
if (p->data[i.y][4+j*2]!=-1 || p->data[i.y][5+j*2]!=-1) {
if (p->newData[i.y][DIV_PAT_FX(j)]!=-1 || p->newData[i.y][DIV_PAT_FXVAL(j)]!=-1) {
effectOrder[placementIndex++]=j;
}
}
for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols; j++) {
if (p->data[i.y][4+j*2]==-1 && p->data[i.y][5+j*2]==-1) {
if (p->newData[i.y][DIV_PAT_FX(j)]==-1 && p->newData[i.y][DIV_PAT_FXVAL(j)]==-1) {
effectOrder[placementIndex++]=j;
}
}
@ -414,7 +371,7 @@ void FurnaceGUI::doReplace() {
case 3: { // insert in free spaces
int placementIndex=0;
for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols && j<8; j++) {
if (p->data[i.y][4+j*2]==-1 && p->data[i.y][5+j*2]==-1) {
if (p->newData[i.y][DIV_PAT_FX(j)]==-1 && p->newData[i.y][DIV_PAT_FXVAL(j)]==-1) {
effectOrder[placementIndex++]=j;
}
}
@ -422,61 +379,66 @@ void FurnaceGUI::doReplace() {
}
}
// then we replace effects/values
for (int j=0; j<queryReplaceEffectCount && j<8; j++) {
signed char pos=effectOrder[j];
// don't replace if we cannot find a position
if (pos==-1) continue;
// replace effect
if (queryReplaceEffectDo[j]) {
switch (queryReplaceEffectMode[j]) {
case GUI_QUERY_REPLACE_SET:
p->data[i.y][4+pos*2]=queryReplaceEffect[j];
p->newData[i.y][DIV_PAT_FX(pos)]=queryReplaceEffect[j];
break;
case GUI_QUERY_REPLACE_ADD:
if (p->data[i.y][4+pos*2]>=0) {
p->data[i.y][4+pos*2]+=queryReplaceEffect[j];
if (p->data[i.y][4+pos*2]<0) p->data[i.y][4+pos*2]=0;
if (p->data[i.y][4+pos*2]>255) p->data[i.y][4+pos*2]=255;
if (p->newData[i.y][DIV_PAT_FX(pos)]>=0) {
p->newData[i.y][DIV_PAT_FX(pos)]+=queryReplaceEffect[j];
if (p->newData[i.y][DIV_PAT_FX(pos)]<0) p->newData[i.y][DIV_PAT_FX(pos)]=0;
if (p->newData[i.y][DIV_PAT_FX(pos)]>255) p->newData[i.y][DIV_PAT_FX(pos)]=255;
}
break;
case GUI_QUERY_REPLACE_ADD_OVERFLOW:
if (p->data[i.y][4+pos*2]>=0) p->data[i.y][4+pos*2]=(p->data[i.y][4+pos*2]+queryReplaceEffect[j])&0xff;
if (p->newData[i.y][DIV_PAT_FX(pos)]>=0) p->newData[i.y][DIV_PAT_FX(pos)]=(p->newData[i.y][DIV_PAT_FX(pos)]+queryReplaceEffect[j])&0xff;
break;
case GUI_QUERY_REPLACE_SCALE:
if (p->data[i.y][4+pos*2]>=0) {
p->data[i.y][4+pos*2]=(p->data[i.y][4+pos*2]*queryReplaceEffect[j])/100;
if (p->data[i.y][4+pos*2]<0) p->data[i.y][4+pos*2]=0;
if (p->data[i.y][4+pos*2]>255) p->data[i.y][4+pos*2]=255;
if (p->newData[i.y][DIV_PAT_FX(pos)]>=0) {
p->newData[i.y][DIV_PAT_FX(pos)]=(p->newData[i.y][DIV_PAT_FX(pos)]*queryReplaceEffect[j])/100;
if (p->newData[i.y][DIV_PAT_FX(pos)]<0) p->newData[i.y][DIV_PAT_FX(pos)]=0;
if (p->newData[i.y][DIV_PAT_FX(pos)]>255) p->newData[i.y][DIV_PAT_FX(pos)]=255;
}
break;
case GUI_QUERY_REPLACE_CLEAR:
p->data[i.y][4+pos*2]=-1;
p->newData[i.y][DIV_PAT_FX(pos)]=-1;
break;
}
}
// replace effect value
if (queryReplaceEffectValDo[j]) {
switch (queryReplaceEffectValMode[j]) {
case GUI_QUERY_REPLACE_SET:
p->data[i.y][5+pos*2]=queryReplaceEffectVal[j];
p->newData[i.y][DIV_PAT_FXVAL(pos)]=queryReplaceEffectVal[j];
break;
case GUI_QUERY_REPLACE_ADD:
if (p->data[i.y][5+pos*2]>=0) {
p->data[i.y][5+pos*2]+=queryReplaceEffectVal[j];
if (p->data[i.y][5+pos*2]<0) p->data[i.y][5+pos*2]=0;
if (p->data[i.y][5+pos*2]>255) p->data[i.y][5+pos*2]=255;
if (p->newData[i.y][DIV_PAT_FXVAL(pos)]>=0) {
p->newData[i.y][DIV_PAT_FXVAL(pos)]+=queryReplaceEffectVal[j];
if (p->newData[i.y][DIV_PAT_FXVAL(pos)]<0) p->newData[i.y][DIV_PAT_FXVAL(pos)]=0;
if (p->newData[i.y][DIV_PAT_FXVAL(pos)]>255) p->newData[i.y][DIV_PAT_FXVAL(pos)]=255;
}
break;
case GUI_QUERY_REPLACE_ADD_OVERFLOW:
if (p->data[i.y][5+pos*2]>=0) p->data[i.y][5+pos*2]=(p->data[i.y][5+pos*2]+queryReplaceEffectVal[j])&0xff;
if (p->newData[i.y][DIV_PAT_FXVAL(pos)]>=0) p->newData[i.y][DIV_PAT_FXVAL(pos)]=(p->newData[i.y][DIV_PAT_FXVAL(pos)]+queryReplaceEffectVal[j])&0xff;
break;
case GUI_QUERY_REPLACE_SCALE:
if (p->data[i.y][5+pos*2]>=0) {
p->data[i.y][5+pos*2]=(p->data[i.y][5+pos*2]*queryReplaceEffectVal[j])/100;
if (p->data[i.y][5+pos*2]<0) p->data[i.y][5+pos*2]=0;
if (p->data[i.y][5+pos*2]>255) p->data[i.y][5+pos*2]=255;
if (p->newData[i.y][DIV_PAT_FXVAL(pos)]>=0) {
p->newData[i.y][DIV_PAT_FXVAL(pos)]=(p->newData[i.y][DIV_PAT_FXVAL(pos)]*queryReplaceEffectVal[j])/100;
if (p->newData[i.y][DIV_PAT_FXVAL(pos)]<0) p->newData[i.y][DIV_PAT_FXVAL(pos)]=0;
if (p->newData[i.y][DIV_PAT_FXVAL(pos)]>255) p->newData[i.y][DIV_PAT_FXVAL(pos)]=255;
}
break;
case GUI_QUERY_REPLACE_CLEAR:
p->data[i.y][5+pos*2]=-1;
p->newData[i.y][DIV_PAT_FXVAL(pos)]=-1;
break;
}
}
@ -484,8 +446,8 @@ void FurnaceGUI::doReplace() {
// issue undo step
for (int j=0; j<DIV_MAX_COLS; j++) {
if (p->data[i.y][j]!=prevVal[j]) {
us.pat.push_back(UndoPatternData(i.subsong,i.x,patIndex,i.y,j,prevVal[j],p->data[i.y][j]));
if (p->newData[i.y][j]!=prevVal[j]) {
us.pat.push_back(UndoPatternData(i.subsong,i.x,patIndex,i.y,j,prevVal[j],p->newData[i.y][j]));
}
}
}
@ -615,15 +577,15 @@ void FurnaceGUI::drawFindReplace() {
ImGui::Combo("##NCondition",&i.noteMode,LocalizedComboGetter,queryModes,GUI_QUERY_MAX);
ImGui::TableNextColumn();
if (FIRST_VISIBLE(i.noteMode)) {
if ((i.noteMode==GUI_QUERY_RANGE || i.noteMode==GUI_QUERY_RANGE_NOT) && i.note>=120) {
i.note=0;
if ((i.noteMode==GUI_QUERY_RANGE || i.noteMode==GUI_QUERY_RANGE_NOT) && i.note>=180) {
i.note=108;
}
NoteSelector(&i.note, i.noteMode!=GUI_QUERY_RANGE && i.noteMode!=GUI_QUERY_RANGE_NOT);
}
ImGui::TableNextColumn();
if (SECOND_VISIBLE(i.noteMode)) {
if (i.noteMax<-60 || i.noteMax>=120) {
i.noteMax=0;
if (i.noteMax<0 || i.noteMax>=256) {
i.noteMax=108;
}
NoteSelector(&i.noteMax, false);
}

View file

@ -97,66 +97,54 @@ void FurnaceGUI::enableSafeMode() {
safeMode=true;
}
const char* FurnaceGUI::noteName(short note, short octave) {
if (note==100) {
const char* FurnaceGUI::noteName(short note) {
if (note==DIV_NOTE_OFF) {
return noteOffLabel;
} else if (note==101) { // note off and envelope release
} else if (note==DIV_NOTE_REL) { // note off and envelope release
return noteRelLabel;
} else if (note==102) { // envelope release only
} else if (note==DIV_MACRO_REL) { // envelope release only
return macroRelLabel;
} else if (octave==0 && note==0) {
} else if (note==-1) {
return emptyLabel;
} else if (note==0 && octave!=0) {
} else if (note==DIV_NOTE_NULL_PAT) {
return "BUG";
}
int seek=(note+(signed char)octave*12)+60;
if (seek<0 || seek>=180) {
if (note<0 || note>=180) {
return "???";
}
if (settings.flatNotes) {
if (settings.germanNotation) return noteNamesGF[seek];
return noteNamesF[seek];
if (settings.germanNotation) return noteNamesGF[note];
return noteNamesF[note];
}
if (settings.germanNotation) return noteNamesG[seek];
return noteNames[seek];
if (settings.germanNotation) return noteNamesG[note];
return noteNames[note];
}
bool FurnaceGUI::decodeNote(const char* what, short& note, short& octave) {
bool FurnaceGUI::decodeNote(const char* what, short& note) {
if (strlen(what)!=3) return false;
if (strcmp(what,"...")==0) {
note=0;
octave=0;
note=-1;
return true;
}
if (strcmp(what,"???")==0) {
note=0;
octave=0;
note=DIV_NOTE_NULL_PAT;
return true;
}
if (strcmp(what,"OFF")==0) {
note=100;
octave=0;
note=DIV_NOTE_OFF;
return true;
}
if (strcmp(what,"===")==0) {
note=101;
octave=0;
note=DIV_NOTE_REL;
return true;
}
if (strcmp(what,"REL")==0) {
note=102;
octave=0;
note=DIV_MACRO_REL;
return true;
}
for (int i=0; i<180; i++) {
if (strcmp(what,noteNames[i])==0) {
if ((i%12)==0) {
note=12;
octave=(unsigned char)((i/12)-6);
} else {
note=i%12;
octave=(unsigned char)((i/12)-5);
}
note=i;
return true;
}
}
@ -573,53 +561,53 @@ bool FurnaceGUI::InvCheckbox(const char* label, bool* value) {
bool FurnaceGUI::NoteSelector(int* value, bool showOffRel, int octaveMin, int octaveMax) {
bool ret=false, calcNote=false;
char tempID[64];
if (*value==130) {
if (*value==DIV_MACRO_REL) {
snprintf(tempID,64,"%s##MREL",macroRelLabel);
} else if (*value==129) {
} else if (*value==DIV_NOTE_REL) {
snprintf(tempID,64,"%s##NREL",noteRelLabel);
} else if (*value==128) {
} else if (*value==DIV_NOTE_OFF) {
snprintf(tempID,64,"%s##NOFF",noteOffLabel);
} else if (*value>=-60 && *value<120) {
snprintf(tempID,64,"%c%c",noteNames[*value%12+72][0],(noteNames[*value%12+72][1]=='-')?' ':noteNames[*value%12+72][1]);
} else if (*value>=0 && *value<180) {
snprintf(tempID,64,"%c%c",noteNames[60+(*value%12)][0],(noteNames[60+(*value%12)][1]=='-')?' ':noteNames[60+(*value%12)][1]);
} else {
snprintf(tempID,64,"???");
*value=0;
}
float width=ImGui::GetContentRegionAvail().x/2-ImGui::GetStyle().FramePadding.x;
ImGui::SetNextItemWidth(width);
int note=(*value+60)%12;
int oct=0;
if (*value<120) oct=(*value-note)/12;
int note=(*value)%12;
int oct=-5;
if (*value<180) oct=(*value-note-60)/12;
ImGui::BeginGroup();
ImGui::PushID(value);
if (ImGui::BeginCombo("##NoteSelectorNote",tempID)) {
for (int j=0; j<12; j++) {
snprintf(tempID,64,"%c%c",noteNames[j+72][0],(noteNames[j+72][1]=='-')?' ':noteNames[j+72][1]);
if (ImGui::Selectable(tempID,note==j && *value<128)) {
snprintf(tempID,64,"%c%c",noteNames[60+j][0],(noteNames[60+j][1]=='-')?' ':noteNames[60+j][1]);
if (ImGui::Selectable(tempID,note==j && *value<180)) {
note=j;
calcNote=true;
}
if (note==j && *value<120) ImGui::SetItemDefaultFocus();
if (note==j && *value<180) ImGui::SetItemDefaultFocus();
}
if (showOffRel) {
if (ImGui::Selectable(noteOffLabel,*value==128)) {
*value=128;
if (ImGui::Selectable(noteOffLabel,*value==DIV_NOTE_OFF)) {
*value=DIV_NOTE_OFF;
ret=true;
}
if (ImGui::Selectable(noteRelLabel,*value==129)) {
*value=129;
if (ImGui::Selectable(noteRelLabel,*value==DIV_NOTE_REL)) {
*value=DIV_NOTE_REL;
ret=true;
}
if (ImGui::Selectable(macroRelLabel,*value==130)) {
*value=130;
if (ImGui::Selectable(macroRelLabel,*value==DIV_MACRO_REL)) {
*value=DIV_MACRO_REL;
ret=true;
}
if (*value>=128 && *value<=130) ImGui::SetItemDefaultFocus();
if (*value>=DIV_NOTE_OFF && *value<=DIV_MACRO_REL) ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (*value<120) {
if (*value<180) {
ImGui::SetNextItemWidth(width/2);
if (ImGui::InputScalar("##NoteSelectorOctave",ImGuiDataType_S32,&oct)) {
if (oct<octaveMin) oct=octaveMin;
@ -628,7 +616,7 @@ bool FurnaceGUI::NoteSelector(int* value, bool showOffRel, int octaveMin, int oc
}
}
if (calcNote) {
*value=oct*12+note;
*value=(oct+5)*12+note;
ret=true;
}
ImGui::PopID();
@ -1395,48 +1383,39 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
prepareUndo(GUI_UNDO_PATTERN_EDIT);
if (key==GUI_NOTE_OFF) { // note off
pat->data[y][0]=100;
pat->data[y][1]=0;
pat->newData[y][DIV_PAT_NOTE]=DIV_NOTE_OFF;
removeIns=true;
} else if (key==GUI_NOTE_OFF_RELEASE) { // note off + env release
pat->data[y][0]=101;
pat->data[y][1]=0;
pat->newData[y][DIV_PAT_NOTE]=DIV_NOTE_REL;
removeIns=true;
} else if (key==GUI_NOTE_RELEASE) { // env release only
pat->data[y][0]=102;
pat->data[y][1]=0;
pat->newData[y][DIV_PAT_NOTE]=DIV_MACRO_REL;
removeIns=true;
} else {
pat->data[y][0]=num%12;
pat->data[y][1]=num/12;
if (pat->data[y][0]==0) {
pat->data[y][0]=12;
pat->data[y][1]--;
}
pat->data[y][1]=(unsigned char)pat->data[y][1];
pat->newData[y][DIV_PAT_NOTE]=num+60;
if (latchIns==-2) {
if (curIns>=(int)e->song.ins.size()) curIns=-1;
if (curIns>=0) {
pat->data[y][2]=curIns;
pat->newData[y][DIV_PAT_INS]=curIns;
}
} else if (latchIns!=-1 && !e->song.ins.empty()) {
pat->data[y][2]=MIN(((int)e->song.ins.size())-1,latchIns);
pat->newData[y][DIV_PAT_INS]=MIN(((int)e->song.ins.size())-1,latchIns);
}
int maxVol=e->getMaxVolumeChan(ch);
if (latchVol!=-1) {
pat->data[y][3]=MIN(maxVol,latchVol);
pat->newData[y][DIV_PAT_VOL]=MIN(maxVol,latchVol);
} else if (vol!=-1) {
pat->data[y][3]=e->mapVelocity(ch,pow((float)vol/127.0f,midiMap.volExp));
pat->newData[y][DIV_PAT_VOL]=e->mapVelocity(ch,pow((float)vol/127.0f,midiMap.volExp));
}
if (latchEffect!=-1) pat->data[y][4]=latchEffect;
if (latchEffectVal!=-1) pat->data[y][5]=latchEffectVal;
if (latchEffect!=-1) pat->newData[y][DIV_PAT_FX(0)]=latchEffect;
if (latchEffectVal!=-1) pat->newData[y][DIV_PAT_FXVAL(0)]=latchEffectVal;
}
if (removeIns) {
if (settings.removeInsOff) {
pat->data[y][2]=-1;
pat->newData[y][DIV_PAT_INS]=-1;
}
if (settings.removeVolOff) {
pat->data[y][3]=-1;
pat->newData[y][DIV_PAT_VOL]=-1;
}
}
editAdvance();
@ -1455,26 +1434,27 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) {
DivPattern* pat=e->curPat[ch].getPattern(e->curOrders->ord[ch][ord],true);
prepareUndo(GUI_UNDO_PATTERN_EDIT);
if (target==-1) target=cursor.xFine+1;
if (target==-1) target=cursor.xFine;
if (direct) {
pat->data[y][target]=num&0xff;
pat->newData[y][target]=num&0xff;
} else {
if (pat->data[y][target]==-1) pat->data[y][target]=0;
if (pat->newData[y][target]==-1) pat->newData[y][target]=0;
if (!settings.pushNibble && !curNibble) {
pat->data[y][target]=num;
pat->newData[y][target]=num;
} else {
pat->data[y][target]=((pat->data[y][target]<<4)|num)&0xff;
pat->newData[y][target]=((pat->newData[y][target]<<4)|num)&0xff;
}
}
if (cursor.xFine==1) { // instrument
if (pat->data[y][target]>=(int)e->song.ins.size()) {
pat->data[y][target]&=0x0f;
if (pat->data[y][target]>=(int)e->song.ins.size()) {
pat->data[y][target]=(int)e->song.ins.size()-1;
// TODO: shouldn't this be target?
if (cursor.xFine==DIV_PAT_INS) { // instrument
if (pat->newData[y][target]>=(int)e->song.ins.size()) {
pat->newData[y][target]&=0x0f;
if (pat->newData[y][target]>=(int)e->song.ins.size()) {
pat->newData[y][target]=(int)e->song.ins.size()-1;
}
}
if (settings.absorbInsInput) {
curIns=pat->data[y][target];
curIns=pat->newData[y][target];
wavePreviewInit=true;
updateFMPreview=true;
}
@ -1490,18 +1470,18 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) {
}
}
makeUndo(GUI_UNDO_PATTERN_EDIT);
} else if (cursor.xFine==2) {
} else if (cursor.xFine==DIV_PAT_VOL) {
if (curNibble) {
if (pat->data[y][target]>e->getMaxVolumeChan(ch)) pat->data[y][target]=e->getMaxVolumeChan(ch);
if (pat->newData[y][target]>e->getMaxVolumeChan(ch)) pat->newData[y][target]=e->getMaxVolumeChan(ch);
} else {
pat->data[y][target]&=15;
pat->newData[y][target]&=15;
}
if (direct) {
curNibble=false;
} else {
if (e->getMaxVolumeChan(ch)<16) {
curNibble=false;
if (pat->data[y][target]>e->getMaxVolumeChan(ch)) pat->data[y][target]=e->getMaxVolumeChan(ch);
if (pat->newData[y][target]>e->getMaxVolumeChan(ch)) pat->newData[y][target]=e->getMaxVolumeChan(ch);
editAdvance();
} else {
curNibble=!curNibble;
@ -2380,7 +2360,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
break;
}
if (hasOpened) curFileDialog=type;
//ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard;
}
int FurnaceGUI::save(String path, int dmfVersion) {
@ -2390,7 +2369,7 @@ int FurnaceGUI::save(String path, int dmfVersion) {
if (dmfVersion<24) dmfVersion=24;
w=e->saveDMF(dmfVersion);
} else {
w=e->saveFur(false,settings.newPatternFormat);
w=e->saveFur(false);
}
if (w==NULL) {
lastError=e->getLastError();
@ -3197,10 +3176,10 @@ void FurnaceGUI::editOptions(bool topMenu) {
ImGui::SameLine();
if (ImGui::Button(_("Set"))) {
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
latchIns=pat->data[cursor.y][2];
latchVol=pat->data[cursor.y][3];
latchEffect=pat->data[cursor.y][4];
latchEffectVal=pat->data[cursor.y][5];
latchIns=pat->newData[cursor.y][DIV_PAT_INS];
latchVol=pat->newData[cursor.y][DIV_PAT_VOL];
latchEffect=pat->newData[cursor.y][DIV_PAT_FX(0)];
latchEffectVal=pat->newData[cursor.y][DIV_PAT_FXVAL(0)];
latchTarget=0;
latchNibble=false;
}
@ -4915,46 +4894,50 @@ bool FurnaceGUI::loop() {
DivPattern* p=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],false);
if (cursor.xFine>=0) switch (cursor.xFine) {
case 0: // note
if (p->data[cursor.y][0]>0) {
if (p->data[cursor.y][0]==100) {
if (p->newData[cursor.y][DIV_PAT_NOTE]>=0) {
if (p->newData[cursor.y][DIV_PAT_NOTE]==DIV_NOTE_OFF) {
info=fmt::sprintf(_("Note off (cut)"));
} else if (p->data[cursor.y][0]==101) {
} else if (p->newData[cursor.y][DIV_PAT_NOTE]==DIV_NOTE_REL) {
info=fmt::sprintf(_("Note off (release)"));
} else if (p->data[cursor.y][0]==102) {
} else if (p->newData[cursor.y][DIV_PAT_NOTE]==DIV_MACRO_REL) {
info=fmt::sprintf(_("Macro release only"));
} else {
info=fmt::sprintf(_("Note on: %s"),noteName(p->data[cursor.y][0],p->data[cursor.y][1]));
info=fmt::sprintf(_("Note on: %s"),noteName(p->newData[cursor.y][DIV_PAT_NOTE]));
}
hasInfo=true;
}
break;
case 1: // instrument
if (p->data[cursor.y][2]>-1) {
if (p->data[cursor.y][2]>=(int)e->song.ins.size()) {
info=fmt::sprintf(_("Ins %d: <invalid>"),p->data[cursor.y][2]);
if (p->newData[cursor.y][DIV_PAT_INS]>-1) {
if (p->newData[cursor.y][DIV_PAT_INS]>=(int)e->song.ins.size()) {
info=fmt::sprintf(_("Ins %d: <invalid>"),p->newData[cursor.y][DIV_PAT_INS]);
} else {
DivInstrument* ins=e->getIns(p->data[cursor.y][2]);
info=fmt::sprintf(_("Ins %d: %s"),p->data[cursor.y][2],ins->name);
DivInstrument* ins=e->getIns(p->newData[cursor.y][DIV_PAT_INS]);
info=fmt::sprintf(_("Ins %d: %s"),p->newData[cursor.y][DIV_PAT_INS],ins->name);
}
hasInfo=true;
}
break;
case 2: // volume
if (p->data[cursor.y][3]>-1) {
if (p->newData[cursor.y][DIV_PAT_VOL]>-1) {
int maxVol=e->getMaxVolumeChan(cursor.xCoarse);
if (maxVol<1 || p->data[cursor.y][3]>maxVol) {
info=fmt::sprintf(_("Set volume: %d (%.2X, INVALID!)"),p->data[cursor.y][3],p->data[cursor.y][3]);
if (maxVol<1 || p->newData[cursor.y][DIV_PAT_VOL]>maxVol) {
info=fmt::sprintf(_("Set volume: %d (%.2X, INVALID!)"),p->newData[cursor.y][DIV_PAT_VOL],p->newData[cursor.y][DIV_PAT_VOL]);
} else {
float realVol=e->getGain(cursor.xCoarse,p->data[cursor.y][3]);
info=fmt::sprintf(_("Set volume: %d (%.2X, %d%%)"),p->data[cursor.y][3],p->data[cursor.y][3],(int)(realVol*100.0f));
float realVol=e->getGain(cursor.xCoarse,p->newData[cursor.y][DIV_PAT_VOL]);
info=fmt::sprintf(_("Set volume: %d (%.2X, %d%%)"),p->newData[cursor.y][DIV_PAT_VOL],p->newData[cursor.y][DIV_PAT_VOL],(int)(realVol*100.0f));
}
hasInfo=true;
}
break;
default: // effect
int actualCursor=((cursor.xFine+1)&(~1));
if (p->data[cursor.y][actualCursor]>-1) {
info=e->getEffectDesc(p->data[cursor.y][actualCursor],cursor.xCoarse,true);
if (cursor.xFine<DIV_MAX_COLS) {
if (p->newData[cursor.y][cursor.xFine]>-1) {
info=e->getEffectDesc(p->newData[cursor.y][cursor.xFine],cursor.xCoarse,true);
hasInfo=true;
}
} else {
info=_("Error!");
hasInfo=true;
}
break;
@ -6536,11 +6519,7 @@ bool FurnaceGUI::loop() {
e->lockEngine([this]() {
for (int i=0; i<e->getTotalChannelCount(); i++) {
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],true);
memset(pat->data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
for (int j=0; j<DIV_MAX_ROWS; j++) {
pat->data[j][0]=0;
pat->data[j][1]=0;
}
memset(pat->newData,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
}
});
MARK_MODIFIED;
@ -7307,7 +7286,7 @@ bool FurnaceGUI::loop() {
}
}
logD("saving backup...");
SafeWriter* w=e->saveFur(true,true);
SafeWriter* w=e->saveFur(true);
logV("writing file...");
if (w!=NULL) {
@ -7982,6 +7961,8 @@ bool FurnaceGUI::init() {
prepareLayout();
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable;
//ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard;
//ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavCaptureKeyboard;
toggleMobileUI(mobileUI,true);
firstFrame=true;
@ -8840,7 +8821,7 @@ FurnaceGUI::FurnaceGUI():
queryReplaceNoteMode(0),
queryReplaceInsMode(0),
queryReplaceVolMode(0),
queryReplaceNote(0),
queryReplaceNote(108),
queryReplaceIns(0),
queryReplaceVol(0),
queryReplaceNoteDo(false),

View file

@ -1467,8 +1467,8 @@ struct FurnaceGUIFindQuery {
insMode(GUI_QUERY_IGNORE),
volMode(GUI_QUERY_IGNORE),
effectCount(0),
note(0),
noteMax(0),
note(108),
noteMax(108),
ins(0),
insMax(0),
vol(0),
@ -2002,7 +2002,6 @@ class FurnaceGUI {
int iCannotWait;
int orderButtonPos;
int compress;
int newPatternFormat;
int renderClearPos;
int insertBehavior;
int pullDeleteRow;
@ -2257,7 +2256,6 @@ class FurnaceGUI {
iCannotWait(0),
orderButtonPos(2),
compress(1),
newPatternFormat(1),
renderClearPos(0),
insertBehavior(1),
pullDeleteRow(1),
@ -3151,9 +3149,9 @@ class FurnaceGUI {
void showWarning(String what, FurnaceGUIWarnings type);
void showError(String what);
String getLastError();
const char* noteNameNormal(short note, short octave);
const char* noteName(short note, short octave);
bool decodeNote(const char* what, short& note, short& octave);
const char* noteNameNormal(short note);
const char* noteName(short note);
bool decodeNote(const char* what, short& note);
void bindEngine(DivEngine* eng);
void enableSafeMode();
void updateScroll(int amount);

View file

@ -1952,7 +1952,7 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s
ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding);
float volY=1.0-((float)vol/15.0);
float lenPos=(sLen>62)?1.0:((float)sLen/384.0);
float lenPos=(sLen>63)?1.0:((float)sLen/384.0);
float envEndPoint=((float)len/7.0)*((float)(dir?(15-vol):vol)/15.0);
ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.0,volY));
@ -1978,10 +1978,10 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s
pos2=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,volY));
}
}
ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,(len>0 || sLen<63)?((dir && sLen>62)?0.0:1.0):volY));
ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,(len>0 || sLen<64)?((dir && sLen>62)?0.0:1.0):volY));
addAALine(dl,pos1,pos2,color);
if (lenPos>=envEndPoint && sLen<63 && dir) {
if (lenPos>=envEndPoint && sLen<64 && dir) {
pos3=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,0.0));
addAALine(dl,pos2,pos3,color);
ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,1.0));
@ -6447,7 +6447,7 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) {
"If this is disabled,filter cutoff will increase if you increase the pitch."));
}
snprintf(buffer2,100,_("%s"),noteNameNormal(filt->bindCutoffToNoteCenter%12,(short)(filt->bindCutoffToNoteCenter / 12)-5));
snprintf(buffer2,100,_("%s"),noteNameNormal(filt->bindCutoffToNoteCenter));
snprintf(buffer,100,_("Cutoff change center note##bindcutcenternote%d"),i+1);
P(CWSliderScalar(buffer,ImGuiDataType_U8,&filt->bindCutoffToNoteCenter,&_ZERO,&_ONE_HUNDRED_SEVENTY_NINE,buffer2)); rightClickable
if (ImGui::IsItemHovered()) {
@ -6480,7 +6480,7 @@ void FurnaceGUI::drawInsSID3(DivInstrument* ins) {
"If this is disabled,filter resonance will increase if you increase the pitch."));
}
snprintf(buffer2,100,_("%s"),noteNameNormal(filt->bindResonanceToNoteCenter%12,(short)(filt->bindResonanceToNoteCenter / 12)-5));
snprintf(buffer2,100,_("%s"),noteNameNormal(filt->bindResonanceToNoteCenter));
snprintf(buffer,100,_("Resonance change center note##bindrescenternote%d"),i+1);
P(CWSliderScalar(buffer,ImGuiDataType_U8,&filt->bindResonanceToNoteCenter,&_ZERO,&_ONE_HUNDRED_SEVENTY_NINE,buffer2)); rightClickable
if (ImGui::IsItemHovered()) {

View file

@ -27,6 +27,7 @@
#include <algorithm>
#include <chrono>
#include <imgui.h>
#include <imgui_internal.h>
#ifdef _WIN32
#include <windows.h>
#include <shlwapi.h>
@ -977,7 +978,6 @@ void FurnaceFilePicker::drawFileList(ImVec2& tableSize, bool& acknowledged) {
// file list
entryLock.lock();
int index=0;
listClipper.Begin(filteredEntries.size(),rowHeight);
while (listClipper.Step()) {
for (int _i=listClipper.DisplayStart; _i<listClipper.DisplayEnd; _i++) {
@ -1003,7 +1003,7 @@ void FurnaceFilePicker::drawFileList(ImVec2& tableSize, bool& acknowledged) {
// name
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(style->color));
ImGui::PushID(index++);
ImGui::PushID(_i);
if (ImGui::Selectable(style->icon.c_str(),i->isSelected,ImGuiSelectableFlags_AllowDoubleClick|ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_SpanAvailWidth)) {
bool doNotAcknowledge=false;
if ((ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) && multiSelect) {
@ -1054,12 +1054,13 @@ void FurnaceFilePicker::drawFileList(ImVec2& tableSize, bool& acknowledged) {
ImGui::PopID();
ImGui::SameLine();
ImGui::TextUnformatted(i->name.c_str());
// why? can't I just not format?
ImGui::TextNoHashHide("%s",i->name.c_str());
// type
if (displayType) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(i->ext.c_str());
ImGui::TextNoHashHide("%s",i->ext.c_str());
}
// size
@ -1172,11 +1173,21 @@ void FurnaceFilePicker::drawBookmarks(ImVec2& tableSize, String& newDir) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushID(200000+index);
if (ImGui::Selectable(iName.c_str(),iPath==path)) {
if (ImGui::Selectable(iName.c_str(),iPath==path,ImGuiSelectableFlags_NoHashTextHide)) {
newDir=iPath;
}
if (ImGui::BeginPopupContextItem("BookmarkOpts")) {
if (ImGui::MenuItem(_("edit"))) {
size_t separator=i.find('\n');
if (separator!=String::npos) {
editingBookmark=index;
newBookmarkName=i.substr(0,separator);
newBookmarkPath=i.substr(separator+1);
}
}
if (ImGui::MenuItem(_("remove"))) {
markedForRemoval=index;
if (iPath==path) isPathBookmarked=false;
}
@ -1189,6 +1200,24 @@ void FurnaceFilePicker::drawBookmarks(ImVec2& tableSize, String& newDir) {
}
ImGui::EndTable();
}
if (editingBookmark>=0 && editingBookmark<(int)bookmarks.size()) {
ImGui::OpenPopup("BookmarkEdit");
}
if (ImGui::BeginPopup("BookmarkEdit",ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
ImGui::Text("Name:");
ImGui::InputText("##BookEditText",&newBookmarkName);
if (ImGui::Button("OK")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if (!ImGui::IsPopupOpen("BookmarkEdit")) {
if (editingBookmark>=0 && editingBookmark<(int)bookmarks.size()) {
bookmarks[editingBookmark]=newBookmarkName+"\n"+newBookmarkPath;
}
editingBookmark=-1;
}
}
bool FurnaceFilePicker::draw(ImGuiWindowFlags winFlags) {
@ -1395,7 +1424,7 @@ bool FurnaceFilePicker::draw(ImGuiWindowFlags winFlags) {
// create button
ImGui::PushID(100000+pathLevel);
ImGui::SameLine();
if (ImGui::Button(nextButton.c_str())) {
if (ImGui::ButtonEx(nextButton.c_str(),ImVec2(0,0),ImGuiButtonFlags_NoHashTextHide)) {
newDir=pathAsOfNow;
}
pathLevel++;
@ -1926,6 +1955,8 @@ FurnaceFilePicker::FurnaceFilePicker():
isPathBookmarked(false),
isSearch(false),
scheduledSort(0),
imguiFlags(0),
editingBookmark(-1),
curFilterType(0),
lastScrollY(0.0f),
enforceScrollY(0),

View file

@ -114,7 +114,7 @@ class FurnaceFilePicker {
bool multiSelect;
bool confirmOverwrite, dirSelect, noClose, isModal, isEmbed, hasSizeConstraints;
bool isPathBookmarked, isSearch;
int scheduledSort, imguiFlags;
int scheduledSort, imguiFlags, editingBookmark;
size_t curFilterType;
float lastScrollY;
int enforceScrollY;

View file

@ -201,8 +201,8 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
bool cursorVol=(cursor.order==ord && cursor.y==i && cursor.xCoarse==j && cursor.xFine==2 && curWindowLast==GUI_WINDOW_PATTERN);
// note
snprintf(id,63,"%.31s###PN_%d_%d",noteName(pat->data[i][0],pat->data[i][1]),i,j);
if (pat->data[i][0]==0 && pat->data[i][1]==0) {
snprintf(id,63,"%.31s###PN_%d_%d",noteName(pat->newData[i][DIV_PAT_NOTE]),i,j);
if (pat->newData[i][DIV_PAT_NOTE]==-1) {
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
} else {
ImGui::PushStyleColor(ImGuiCol_Text,activeColor);
@ -234,21 +234,21 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
// the following is only visible when the channel is not collapsed
if (e->curSubSong->chanCollapse[j]<3) {
// instrument
if (pat->data[i][2]==-1) {
if (pat->newData[i][DIV_PAT_INS]==-1) {
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
snprintf(id,63,"%.31s###PI_%d_%d",emptyLabel2,i,j);
} else {
if (pat->data[i][2]<0 || pat->data[i][2]>=e->song.insLen) {
if (pat->newData[i][DIV_PAT_INS]<0 || pat->newData[i][DIV_PAT_INS]>=e->song.insLen) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS_ERROR]);
} else {
DivInstrumentType t=e->song.ins[pat->data[i][2]]->type;
DivInstrumentType t=e->song.ins[pat->newData[i][DIV_PAT_INS]]->type;
if (t!=DIV_INS_AMIGA && t!=e->getPreferInsType(j)) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS_WARN]);
} else {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]);
}
}
snprintf(id,63,"%.2X###PI_%d_%d",pat->data[i][2],i,j);
snprintf(id,63,"%.2X###PI_%d_%d",pat->newData[i][DIV_PAT_INS],i,j);
}
ImGui::SameLine(0.0f,0.0f);
if (cursorIns) {
@ -278,14 +278,14 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
if (e->curSubSong->chanCollapse[j]<2) {
// volume
if (pat->data[i][3]==-1) {
if (pat->newData[i][DIV_PAT_VOL]==-1) {
snprintf(id,63,"%.31s###PV_%d_%d",emptyLabel2,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
} else {
int volColor=(pat->data[i][3]*127)/chanVolMax;
int volColor=(pat->newData[i][DIV_PAT_VOL]*127)/chanVolMax;
if (volColor>127) volColor=127;
if (volColor<0) volColor=0;
snprintf(id,63,"%.2X###PV_%d_%d",pat->data[i][3],i,j);
snprintf(id,63,"%.2X###PV_%d_%d",pat->newData[i][DIV_PAT_VOL],i,j);
ImGui::PushStyleColor(ImGuiCol_Text,volColors[volColor]);
}
ImGui::SameLine(0.0f,0.0f);
@ -317,26 +317,27 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
if (e->curSubSong->chanCollapse[j]<1) {
// effects
for (int k=0; k<e->curPat[j].effectCols; k++) {
int index=4+(k<<1);
bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum);
bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum);
bool cursorEffect=(cursor.order==ord && cursor.y==i && cursor.xCoarse==j && cursor.xFine==index-1 && curWindowLast==GUI_WINDOW_PATTERN);
bool cursorEffectVal=(cursor.order==ord && cursor.y==i && cursor.xCoarse==j && cursor.xFine==index && curWindowLast==GUI_WINDOW_PATTERN);
int index=DIV_PAT_FX(k);
int indexVal=DIV_PAT_FXVAL(k);
bool selectedEffect=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum);
bool selectedEffectVal=selectedRow && (j32+indexVal>=sel1XSum && j32+indexVal<=sel2XSum);
bool cursorEffect=(cursor.order==ord && cursor.y==i && cursor.xCoarse==j && cursor.xFine==index && curWindowLast==GUI_WINDOW_PATTERN);
bool cursorEffectVal=(cursor.order==ord && cursor.y==i && cursor.xCoarse==j && cursor.xFine==indexVal && curWindowLast==GUI_WINDOW_PATTERN);
// effect
if (pat->data[i][index]==-1) {
if (pat->newData[i][index]==-1) {
snprintf(id,63,"%.31s###PE%d_%d_%d",emptyLabel2,k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
} else {
if (pat->data[i][index]>0xff) {
if (pat->newData[i][index]>0xff) {
snprintf(id,63,"??###PE%d_%d_%d",k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else if (pat->data[i][index]>=0x10 || settings.oneDigitEffects==0) {
const unsigned char data=pat->data[i][index];
} else if (pat->newData[i][index]>=0x10 || settings.oneDigitEffects==0) {
const unsigned char data=pat->newData[i][index];
snprintf(id,63,"%.2X###PE%d_%d_%d",data,k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
} else {
const unsigned char data=pat->data[i][index];
const unsigned char data=pat->newData[i][index];
snprintf(id,63," %.1X###PE%d_%d_%d",data,k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
}
@ -354,10 +355,10 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
if (selectedEffect) ImGui::PopStyleColor();
}
if (ImGui::IsItemClicked()) {
startSelection(j,index-1,i,ord);
startSelection(j,index,i,ord);
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) {
updateSelection(j,index-1,i,ord);
updateSelection(j,index,i,ord);
}
if (ImGui::IsItemActive() && CHECK_LONG_HOLD) {
ImGui::InhibitInertialScroll();
@ -366,10 +367,10 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
}
// effect value
if (pat->data[i][index+1]==-1) {
if (pat->newData[i][indexVal]==-1) {
snprintf(id,63,"%.31s###PF%d_%d_%d",emptyLabel2,k,i,j);
} else {
snprintf(id,63,"%.2X###PF%d_%d_%d",pat->data[i][index+1],k,i,j);
snprintf(id,63,"%.2X###PF%d_%d_%d",pat->newData[i][indexVal],k,i,j);
}
ImGui::SameLine(0.0f,0.0f);
if (cursorEffectVal) {
@ -384,10 +385,10 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
if (selectedEffectVal) ImGui::PopStyleColor();
}
if (ImGui::IsItemClicked()) {
startSelection(j,index,i,ord);
startSelection(j,indexVal,i,ord);
}
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) {
updateSelection(j,index,i,ord);
updateSelection(j,indexVal,i,ord);
}
if (ImGui::IsItemActive() && CHECK_LONG_HOLD) {
ImGui::InhibitInertialScroll();

View file

@ -853,15 +853,6 @@ void FurnaceGUI::drawSettings() {
settingsChanged=true;
}
bool newPatternFormatB=settings.newPatternFormat;
if (ImGui::Checkbox(_("Use new pattern format when saving"),&newPatternFormatB)) {
settings.newPatternFormat=newPatternFormatB;
settingsChanged=true;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(_("use a packed format which saves space when saving songs.\ndisable if you need compatibility with older Furnace and/or tools\nwhich do not support this format."));
}
bool noDMFCompatB=settings.noDMFCompat;
if (ImGui::Checkbox(_("Don't apply compatibility flags when loading .dmf"),&noDMFCompatB)) {
settings.noDMFCompat=noDMFCompatB;
@ -4898,7 +4889,6 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
settings.noMaximizeWorkaround=conf.getInt("noMaximizeWorkaround",0);
settings.compress=conf.getInt("compress",1);
settings.newPatternFormat=conf.getInt("newPatternFormat",1);
settings.newSongBehavior=conf.getInt("newSongBehavior",0);
settings.playOnLoad=conf.getInt("playOnLoad",0);
settings.centerPopup=conf.getInt("centerPopup",1);
@ -5368,7 +5358,6 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
clampSetting(settings.iCannotWait,0,1);
clampSetting(settings.orderButtonPos,0,2);
clampSetting(settings.compress,0,1);
clampSetting(settings.newPatternFormat,0,1);
clampSetting(settings.renderClearPos,0,1);
clampSetting(settings.insertBehavior,0,1);
clampSetting(settings.pullDeleteRow,0,1);
@ -5500,7 +5489,6 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
conf.set("noMaximizeWorkaround",settings.noMaximizeWorkaround);
conf.set("compress",settings.compress);
conf.set("newPatternFormat",settings.newPatternFormat);
conf.set("newSongBehavior",settings.newSongBehavior);
conf.set("playOnLoad",settings.playOnLoad);
conf.set("centerPopup",settings.centerPopup);