pattern data refactor, part 7

This commit is contained in:
tildearrow 2025-10-16 05:11:09 -05:00
parent 6c804c3674
commit 2b745cac62
6 changed files with 212 additions and 338 deletions

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) {